Close(); } abstract public function Close(): void; abstract public function Open(string $Address, int $Port, int $Timeout, int $Engine): void; abstract public function Write(int $Header, string $String = ''): bool; abstract public function Read(int $Length = 1400): Buffer; protected function ReadInternal(Buffer $Buffer, int $Length, callable $SherlockFunction): Buffer { if ($Buffer->Remaining() === 0) { throw new InvalidPacketException('Failed to read any data from socket', InvalidPacketException::BUFFER_EMPTY); } $Header = $Buffer->GetLong(); if ($Header === -1) { // Single packet // We don't have to do anything } elseif ($Header === -2) { // Split packet $Packets = []; $IsCompressed = false; $ReadMore = false; $PacketChecksum = null; do { $RequestID = $Buffer->GetLong(); switch ($this->Engine) { case SourceQuery::GOLDSOURCE: { $PacketCountAndNumber = $Buffer->GetByte(); $PacketCount = $PacketCountAndNumber & 0xF; $PacketNumber = $PacketCountAndNumber >> 4; break; } case SourceQuery::SOURCE: { $IsCompressed = ($RequestID & 0x80000000) !== 0; $PacketCount = $Buffer->GetByte(); $PacketNumber = $Buffer->GetByte() + 1; if ($IsCompressed) { $Buffer->GetLong(); // Split size $PacketChecksum = $Buffer->GetUnsignedLong(); } else { $Buffer->GetShort(); // Split size } break; } default: { throw new SocketException('Unknown engine.', SocketException::INVALID_ENGINE); } } $Packets[ $PacketNumber ] = $Buffer->Get(); $ReadMore = $PacketCount > sizeof($Packets); } while ($ReadMore && $SherlockFunction($Buffer, $Length)); $Data = implode($Packets); // TODO: Test this if ($IsCompressed) { // Let's make sure this function exists, it's not included in PHP by default if (!function_exists('bzdecompress')) { throw new \RuntimeException('Received compressed packet, PHP doesn\'t have Bzip2 library installed, can\'t decompress.'); } $Data = bzdecompress($Data); if (!is_string($Data) || crc32($Data) !== $PacketChecksum) { throw new InvalidPacketException('CRC32 checksum mismatch of uncompressed packet data.', InvalidPacketException::CHECKSUM_MISMATCH); } } $Buffer->Set(substr($Data, 4)); } else { throw new InvalidPacketException('Socket read: Raw packet header mismatch. (0x' . dechex($Header) . ')', InvalidPacketException::PACKET_HEADER_MISMATCH); } return $Buffer; } }