/** * Process the response * * @return array * @throws \GameQ\Exception\Protocol */ public function processResponse() { // Will hold the packets after sorting $packets = []; // We need to pre-sort these for split packets so we can do extra work where needed foreach ($this->packets_response as $response) { $buffer = new Buffer($response); // Pull out the header $header = $buffer->read(5); // Add the packet to the proper section, we will combine later $packets[$header][] = $buffer->getBuffer(); } unset($buffer); $results = []; // Now let's iterate and process foreach ($packets as $header => $packetGroup) { // Figure out which packet response this is if (!array_key_exists($header, $this->responses)) { throw new Exception(__METHOD__ . " response type '{$header}' is not valid"); } // Now we need to call the proper method $results = array_merge($results, call_user_func_array([$this, $this->responses[$header]], [new Buffer(implode($packetGroup))])); } unset($packets); return $results; }
/** * Process the response * * @return array * @throws \GameQ\Exception\Protocol */ public function processResponse() { // Make a new buffer out of all of the packets $buffer = new Buffer(implode('', $this->packets_response)); // Check the header TS3 if (($header = trim($buffer->readString("\n"))) !== 'TS3') { throw new Exception(__METHOD__ . " Expected header '{$header}' does not match expected 'TS3'."); } // Convert all the escaped characters $raw = str_replace(['\\\\', '\\/'], ['\\', '/'], $buffer->getBuffer()); // Explode the sections and filter to remove empty, junk ones $sections = array_filter(explode("\n", $raw), function ($value) { $value = trim($value); // Not empty string or a message response for "error id=\d" return !empty($value) && substr($value, 0, 5) !== 'error'; }); // Trim up the values to remove extra whitespace $sections = array_map('trim', $sections); // Set the result to a new result instance $result = new Result(); // Iterate over the sections and offload the parsing foreach ($sections as $section) { // Grab a snip of the data so we can figure out what it is $check = substr(trim($section), 0, 4); // Use the first part of the response to figure out where we need to go if ($check == 'virt') { // Server info $this->processDetails($section, $result); } elseif ($check == 'cid=') { // Channels $this->processChannels($section, $result); } elseif ($check == 'clid') { // Clients (players) $this->processPlayers($section, $result); } } unset($buffer, $sections, $section, $check); return $result->fetch(); }
/** * Handles processing the player and team data into a usable format * * @param \GameQ\Buffer $buffer * @param \GameQ\Result $result */ protected function processPlayersAndTeams(Buffer &$buffer, Result &$result) { /* * Explode the data into groups. First is player, next is team (item_t) * Each group should be as follows: * * [0] => item_ * [1] => information for item_ * ... */ $data = explode("", $buffer->getBuffer()); // By default item_group is blank, this will be set for each loop thru the data $item_group = ''; // By default the item_type is blank, this will be set on each loop $item_type = ''; // Loop through all of the $data for information and pull it out into the result for ($x = 0; $x < count($data) - 1; $x++) { // Pull out the item $item = $data[$x]; // If this is an empty item, move on if ($item == '' || $item == "") { continue; } /* * Left as reference: * * Each block of player_ and team_t have preceding junk chars * * player_ is actually \x01player_ * team_t is actually \x00\x02team_t * * Probably a by-product of the change to exploding the data from the original. * * For now we just strip out these characters */ // Check to see if $item has a _ at the end, this is player info if (substr($item, -1) == '_') { // Set the item group $item_group = 'players'; // Set the item type, rip off any trailing stuff and bad chars $item_type = rtrim(str_replace("", '', $item), '_'); } elseif (substr($item, -2) == '_t') { // Check to see if $item has a _t at the end, this is team info // Set the item group $item_group = 'teams'; // Set the item type, rip off any trailing stuff and bad chars $item_type = rtrim(str_replace(["", ""], '', $item), '_t'); } else { // We can assume it is data belonging to a previously defined item // Make a temp buffer so we have easier access to the data $buf_temp = new Buffer($item, Buffer::NUMBER_TYPE_BIGENDIAN); // Get the values while ($buf_temp->getLength()) { // No value so break the loop, end of string if (($val = $buf_temp->readString()) === '') { break; } // Add the value to the proper item in the correct group $result->addSub($item_group, $item_type, utf8_encode(trim($val))); } // Unset our buffer unset($buf_temp); } } // Free up some memory unset($data, $item, $item_group, $item_type, $val); }
/** * Process the response for the StarMade server * * @return array * @throws \GameQ\Exception\Protocol */ public function processResponse() { // Holds the results sent back $results = []; // Holds the processed packets after having been reassembled $processed = []; // Start up the index for the processed $sequence_id_last = 0; foreach ($this->packets_response as $packet) { // Create a new buffer $buffer = new Buffer($packet); // Each "good" packet begins with sequence_id (32-bit) $sequence_id = $buffer->readInt32(); // Sequence id is a response if (array_key_exists($sequence_id, $this->responses)) { $processed[$sequence_id] = $buffer->getBuffer(); $sequence_id_last = $sequence_id; } else { // This is a continuation of the previous packet, reset the buffer and append $buffer->jumpto(0); // Append $processed[$sequence_id_last] .= $buffer->getBuffer(); } } unset($buffer, $sequence_id_last, $sequence_id); // Iterate over the combined packets and do some work foreach ($processed as $sequence_id => $data) { // Create a new buffer $buffer = new Buffer($data); // Get the length of the packet $packetLength = $buffer->getLength(); // Check to make sure the expected length matches the real length // Subtract 4 for the sequence_id pulled out earlier if ($packetLength != $buffer->readInt32() - 4) { throw new Exception(__METHOD__ . " packet length does not match expected length!"); } // Now we need to call the proper method $results = array_merge($results, call_user_func_array([$this, $this->responses[$sequence_id]], [$buffer])); } return $results; }
/** * Process the split packets and decompress if necessary * * @SuppressWarnings(PHPMD.UnusedLocalVariable) * * @param $packet_id * @param array $packets * * @return string * @throws \GameQ\Exception\Protocol */ protected function processPackets($packet_id, array $packets = []) { // Init array so we can order $packs = []; // We have multiple packets so we need to get them and order them foreach ($packets as $i => $packet) { // Make a buffer so we can read this info $buffer = new Buffer($packet); // Gold source if ($this->source_engine == self::GOLDSOURCE_ENGINE) { // Grab the packet number (byte) $packet_number = $buffer->readInt8(); // We need to burn the extra header (\xFF\xFF\xFF\xFF) on first loop if ($i == 0) { $buffer->read(4); } // Now add the rest of the packet to the new array with the packet_number as the id so we can order it $packs[$packet_number] = $buffer->getBuffer(); } else { // Number of packets in this set (byte) $buffer->readInt8(); // The current packet number (byte) $packet_number = $buffer->readInt8(); // Check to see if this is compressed // @todo: Check to make sure these decompress correctly, new changes may affect this loop. if ($packet_id & 2147483648.0) { // Check to see if we have Bzip2 installed if (!function_exists('bzdecompress')) { throw new Exception('Bzip2 is not installed. See http://www.php.net/manual/en/book.bzip2.php for more info.', 0); } // Get the length of the packet (long) $packet_length = $buffer->readInt32Signed(); // Checksum for the decompressed packet (long) $buffer->readInt32Signed(); // Try to decompress $result = bzdecompress($buffer->getBuffer()); // Now verify the length if (strlen($result) != $packet_length) { throw new Exception("Checksum for compressed packet failed! Length expected: {$packet_length}, length\n returned: " . strlen($result)); } } else { // Get the packet length (short) $buffer->readInt16Signed(); // We need to burn the extra header (\xFF\xFF\xFF\xFF) on first loop if ($i == 0) { $buffer->read(4); } // Grab the rest of the buffer as a result $result = $buffer->getBuffer(); } // Add this packet to the list $packs[$packet_number] = $result; } unset($buffer); } // Free some memory unset($packets, $packet); // Sort the packets by packet number ksort($packs); // Now combine the packs into one and return return implode("", $packs); }
/** * Process the response * * @return array * @throws \GameQ\Exception\Protocol */ public function processResponse() { // Make a new buffer out of all of the packets $buffer = new Buffer(implode('', $this->packets_response)); // Check the header [TS] if (($header = trim($buffer->readString("\n"))) !== '[TS]') { throw new Exception(__METHOD__ . " Expected header '{$header}' does not match expected '[TS]'."); } // Split this buffer as the data blocks are bound by "OK" and drop any empty values $sections = array_filter(explode("OK", $buffer->getBuffer()), function ($value) { $value = trim($value); return !empty($value); }); // Trim up the values to remove extra whitespace $sections = array_map('trim', $sections); // Set the result to a new result instance $result = new Result(); // Now we need to iterate over the sections and off load the processing foreach ($sections as $section) { // Grab a snip of the data so we can figure out what it is $check = substr($section, 0, 7); // Offload to the proper method if ($check == 'server_') { // Server settings and info $this->processDetails($section, $result); } elseif ($check == "id\tcode") { // Channel info $this->processChannels($section, $result); } elseif ($check == "p_id\tc_") { // Player info $this->processPlayers($section, $result); } } unset($buffer, $sections, $section, $check); return $result->fetch(); }
/** * Handle processing the status buffer * * @param Buffer $buffer * * @return array */ protected function processStatus(Buffer $buffer) { // Set the result to a new result instance $result = new Result(); // By default dedicted $result->add('dedicated', 1); // Lets peek and see if the data starts with a \ if ($buffer->lookAhead(1) == '\\') { // Burn the first one $buffer->skip(1); } // Explode the data $data = explode('\\', $buffer->getBuffer()); // No longer needed unset($buffer); // Init some vars $numPlayers = 0; $numTeams = 0; $itemCount = count($data); // Now lets loop the array for ($x = 0; $x < $itemCount; $x += 2) { // Set some local vars $key = $data[$x]; $val = $data[$x + 1]; // Check for <variable>_<count> variable (i.e players) if (($suffix = strrpos($key, '_')) !== false && is_numeric(substr($key, $suffix + 1))) { // See if this is a team designation if (substr($key, 0, $suffix) == 'teamname') { $result->addTeam('teamname', $val); $numTeams++; } else { // Its a player if (substr($key, 0, $suffix) == 'playername') { $numPlayers++; } $result->addPlayer(substr($key, 0, $suffix), utf8_encode($val)); } } else { // Regular variable so just add the value. $result->add($key, $val); } } // Add the player and team count $result->add('num_players', $numPlayers); $result->add('num_teams', $numTeams); // Unset some stuff to free up memory unset($data, $key, $val, $suffix, $x, $itemCount); // Return the result return $result->fetch(); }