server = $server; $this->player = $player; } public function handleDataPacket(DataPacket $packet){ if(!$this->player->isConnected()){ return; } $timings = Timings::getReceiveDataPacketTimings($packet); $timings->startTiming(); $packet->decode(); if(!$packet->feof() and !$packet->mayHaveUnreadBytes()){ $remains = substr($packet->buffer, $packet->offset); $this->server->getLogger()->debug("Still " . strlen($remains) . " bytes unread in " . $packet->getName() . ": 0x" . bin2hex($remains)); } $ev = new DataPacketReceiveEvent($this->player, $packet); $ev->call(); if(!$ev->isCancelled() and !$packet->handle($this)){ $this->server->getLogger()->debug("Unhandled " . $packet->getName() . " received from " . $this->player->getName() . ": " . base64_encode($packet->buffer)); } $timings->stopTiming(); } public function handleLogin(LoginPacket $packet) : bool{ return $this->player->handleLogin($packet); } public function handleClientToServerHandshake(ClientToServerHandshakePacket $packet) : bool{ return false; //TODO } public function handleResourcePackClientResponse(ResourcePackClientResponsePacket $packet) : bool{ return $this->player->handleResourcePackClientResponse($packet); } public function handleText(TextPacket $packet) : bool{ if($packet->type === TextPacket::TYPE_CHAT){ return $this->player->chat($packet->message); } return false; } public function handleMovePlayer(MovePlayerPacket $packet) : bool{ return $this->player->handleMovePlayer($packet); } public function handleLevelSoundEventPacketV1(LevelSoundEventPacketV1 $packet) : bool{ return true; //useless leftover from 1.8 } public function handleActorEvent(ActorEventPacket $packet) : bool{ return $this->player->handleEntityEvent($packet); } public function handleInventoryTransaction(InventoryTransactionPacket $packet) : bool{ return $this->player->handleInventoryTransaction($packet); } public function handleMobEquipment(MobEquipmentPacket $packet) : bool{ return $this->player->handleMobEquipment($packet); } public function handleMobArmorEquipment(MobArmorEquipmentPacket $packet) : bool{ return true; //Not used } public function handleInteract(InteractPacket $packet) : bool{ return $this->player->handleInteract($packet); } public function handleBlockPickRequest(BlockPickRequestPacket $packet) : bool{ return $this->player->handleBlockPickRequest($packet); } public function handleActorPickRequest(ActorPickRequestPacket $packet) : bool{ return false; //TODO } public function handlePlayerAction(PlayerActionPacket $packet) : bool{ return $this->player->handlePlayerAction($packet); } public function handleActorFall(ActorFallPacket $packet) : bool{ return true; //Not used } public function handleAnimate(AnimatePacket $packet) : bool{ return $this->player->handleAnimate($packet); } public function handleRespawn(RespawnPacket $packet) : bool{ return $this->player->handleRespawn($packet); } public function handleContainerClose(ContainerClosePacket $packet) : bool{ return $this->player->handleContainerClose($packet); } public function handlePlayerHotbar(PlayerHotbarPacket $packet) : bool{ return true; //this packet is useless } public function handleCraftingEvent(CraftingEventPacket $packet) : bool{ return true; //this is a broken useless packet, so we don't use it } public function handleAdventureSettings(AdventureSettingsPacket $packet) : bool{ return $this->player->handleAdventureSettings($packet); } public function handleBlockActorData(BlockActorDataPacket $packet) : bool{ return $this->player->handleBlockEntityData($packet); } public function handlePlayerInput(PlayerInputPacket $packet) : bool{ return false; //TODO } public function handleSetPlayerGameType(SetPlayerGameTypePacket $packet) : bool{ return $this->player->handleSetPlayerGameType($packet); } public function handleSpawnExperienceOrb(SpawnExperienceOrbPacket $packet) : bool{ return false; //TODO } public function handleMapInfoRequest(MapInfoRequestPacket $packet) : bool{ return false; //TODO } public function handleRequestChunkRadius(RequestChunkRadiusPacket $packet) : bool{ $this->player->setViewDistance($packet->radius); return true; } public function handleItemFrameDropItem(ItemFrameDropItemPacket $packet) : bool{ return $this->player->handleItemFrameDropItem($packet); } public function handleBossEvent(BossEventPacket $packet) : bool{ return false; //TODO } public function handleShowCredits(ShowCreditsPacket $packet) : bool{ return false; //TODO: handle resume } public function handleCommandRequest(CommandRequestPacket $packet) : bool{ return $this->player->chat($packet->command); } public function handleCommandBlockUpdate(CommandBlockUpdatePacket $packet) : bool{ return false; //TODO } public function handleResourcePackChunkRequest(ResourcePackChunkRequestPacket $packet) : bool{ return $this->player->handleResourcePackChunkRequest($packet); } public function handlePlayerSkin(PlayerSkinPacket $packet) : bool{ return $this->player->changeSkin(SkinAdapterSingleton::get()->fromSkinData($packet->skin), $packet->newSkinName, $packet->oldSkinName); } public function handleBookEdit(BookEditPacket $packet) : bool{ return $this->player->handleBookEdit($packet); } public function handleModalFormResponse(ModalFormResponsePacket $packet) : bool{ return $this->player->onFormSubmit($packet->formId, self::stupid_json_decode($packet->formData, true)); } /** * Hack to work around a stupid bug in Minecraft W10 which causes empty strings to be sent unquoted in form responses. * * @param string $json * @param bool $assoc * * @return mixed */ private static function stupid_json_decode(string $json, bool $assoc = false){ if(preg_match('/^\[(.+)\]$/s', $json, $matches) > 0){ $raw = $matches[1]; $lastComma = -1; $newParts = []; $quoteType = null; for($i = 0, $len = strlen($raw); $i <= $len; ++$i){ if($i === $len or ($raw[$i] === "," and $quoteType === null)){ $part = substr($raw, $lastComma + 1, $i - ($lastComma + 1)); if(trim($part) === ""){ //regular parts will have quotes or something else that makes them non-empty $part = '""'; } $newParts[] = $part; $lastComma = $i; }elseif($raw[$i] === '"'){ if($quoteType === null){ $quoteType = $raw[$i]; }elseif($raw[$i] === $quoteType){ for($backslashes = 0; $backslashes < $i && $raw[$i - $backslashes - 1] === "\\"; ++$backslashes){} if(($backslashes % 2) === 0){ //unescaped quote $quoteType = null; } } } } $fixed = "[" . implode(",", $newParts) . "]"; if(($ret = json_decode($fixed, $assoc)) === null){ throw new \InvalidArgumentException("Failed to fix JSON: " . json_last_error_msg() . "(original: $json, modified: $fixed)"); } return $ret; } return json_decode($json, $assoc); } public function handleServerSettingsRequest(ServerSettingsRequestPacket $packet) : bool{ return false; //TODO: GUI stuff } public function handleSetLocalPlayerAsInitialized(SetLocalPlayerAsInitializedPacket $packet) : bool{ $this->player->doFirstSpawn(); return true; } public function handleLevelSoundEvent(LevelSoundEventPacket $packet) : bool{ return $this->player->handleLevelSoundEvent($packet); } public function handleNetworkStackLatency(NetworkStackLatencyPacket $packet) : bool{ return true; //TODO: implement this properly - this is here to silence debug spam from MCPE dev builds } }