public function encodePacket() { $packetIDVarInt = VarInt::writeUnsignedVarInt($this->getPacketID()); $encodedPacket = $this->encodeContents(); $packetLengthVarInt = VarInt::writeUnsignedVarInt(strlen($encodedPacket) + $packetIDVarInt->getDataLength()); return $packetLengthVarInt->getEncoded() . $packetIDVarInt->getEncoded() . $encodedPacket; }
/** * Parse the raw data into the packet * @param $data String the raw data to parse (minus packet ID and packet length */ public function fromRawData($data) { $secretLength = VarInt::readUnsignedVarInt($data); $data = substr($data, $secretLength->getDataLength()); $secretData = substr($data, 0, $secretLength->getValue()); $data = substr($data, $secretLength->getValue()); $verifyLength = VarInt::readUnsignedVarInt($data); $data = substr($data, $verifyLength->getDataLength()); $verifyData = substr($data, 0, $verifyLength->getValue()); $this->setToken($verifyData)->setSecret($secretData); }
/** * Called whenever there is data available on the stream * @param $data String the raw data */ public function onData($data) { //if we're in encryption stage decrypt it first if ($this->secret !== null) { mcrypt_generic_init($this->encryptor, $this->secret, $this->secret); $data = mdecrypt_generic($this->encryptor, $data); mcrypt_generic_deinit($this->encryptor); } //attempt to parse the data as a packet try { //add the data to the current buffer $this->packetBuffer .= $data; //for as long as we have data left in the buffer do { //read the packet length from the stream $packetLengthVarInt = VarInt::readUnsignedVarInt($this->packetBuffer); //not enough data to read the packet length, wait for more data if ($packetLengthVarInt === false) { return; } //total length of the packet is the length of the varint + it's value $totalLength = $packetLengthVarInt->getDataLength() + $packetLengthVarInt->getValue(); //if we don't have enough data to read the entire packet wait for more data to enter $bufferLength = strlen($this->packetBuffer); if ($bufferLength < $totalLength) { return; } //remove the packet length varint from the buffer $this->packetBuffer = substr($this->packetBuffer, $packetLengthVarInt->getDataLength()); //read the packet ID from the buffer $packetDataWithPacketID = substr($this->packetBuffer, 0, $packetLengthVarInt->getValue()); //remove the rest of the packet from the buffer $this->packetBuffer = substr($this->packetBuffer, $packetLengthVarInt->getValue()); //read the packet ID $packetID = VarInt::readUnsignedVarInt($packetDataWithPacketID); //get the raw packet data $packetData = substr($packetDataWithPacketID, $packetID->getDataLength()); $this->lastPacket = $this->currentTime(); //trigger packet processing $this->processPacket($packetID->getValue(), $packetData); //if we have buffer left run again } while (strlen($this->packetBuffer) > 0); //if any exceptions are thrown (error parsing the packets e.t.c.) send a disconnect packet } catch (Exception $ex) { echo "EXCEPTION IN PACKET PARSING {$ex->getMessage()}\n"; echo $ex->getTraceAsString(); $dis = new DisconnectPacket(); $dis->setReason('Internal Server Error: ' . $ex->getMessage()); $this->end($dis->encodePacket()); } }
/** * Get the encoded contents of the packet (minus packetID/length) * @return String */ protected function encodeContents() { $serverIDEncoded = StringType::write($this->serverID); $encodedPublicKey = base64_decode($this->getPublicKey()); $publicKeyLength = VarInt::writeUnsignedVarInt(strlen($encodedPublicKey)); $encodedToken = $this->getToken(); $tokenLength = VarInt::writeUnsignedVarInt(strlen($encodedToken)); return $serverIDEncoded->getEncoded() . $publicKeyLength->getEncoded() . $encodedPublicKey . $tokenLength->getEncoded() . $encodedToken; }
/** * Parse the raw data * @param $data String the raw data to parse (minus packet ID and packet length * @throws InvalidDataException */ function fromRawData($data) { $versionVarInt = VarInt::readUnsignedVarInt($data); $data = substr($data, $versionVarInt->getDataLength()); $addressStringLength = VarInt::readUnsignedVarInt($data); $data = substr($data, $addressStringLength->getDataLength()); $address = substr($data, 0, $addressStringLength->getValue()); $data = substr($data, $addressStringLength->getValue()); $portShort = unpack('nshort', substr($data, 0, 2))['short']; $data = substr($data, 2); $nextVarInt = VarInt::readUnsignedVarInt($data); try { $nextStage = Stage::get($nextVarInt->getValue()); //disconnect if not a valid stage if ($nextStage != Stage::LOGIN() && $nextStage != Stage::STATUS()) { throw new InvalidDataException('Stage must be LOGIN or STATUS on handshake'); } //set all of the data $this->setNextStage($nextStage)->setServerPort($portShort)->setProtocolVersion($versionVarInt->getValue())->setServerAddress($address); } catch (InvalidArgumentException $ex) { throw new InvalidDataException('Stage is not a valid number'); } }