/** * Overload the parse players because the data coming back is different * @see GameQ_Protocols_Quake3::parsePlayers() */ protected function parsePlayers(GameQ_Result &$result, $players_info) { // Explode the arrays out $players = explode("\n", $players_info); // Remove the last array item as it is junk array_pop($players); // Add total number of players $result->add('num_players', count($players)); // Loop the players foreach ($players as $player_info) { $buf = new GameQ_Buffer($player_info); // Add player info $result->addPlayer('frags', $buf->readString(" ")); $result->addPlayer('ping', $buf->readString(" ")); // Skip first " $buf->skip(1); // Add player name $result->addPlayer('name', trim($buf->readString('"'))); // Skip space $buf->skip(1); // Add team $result->addPlayer('team', $buf->read()); } // Free some memory unset($buf, $players, $player_info); }
public function preprocess($packets) { $result = array(); // Get packet index, remove header foreach ($packets as $packet) { $p = new GameQ_Buffer($packet); $p->skip(14); $cur_packet = $p->readInt16(); $result[$cur_packet] = $p->getBuffer(); } // Sort packets, reset index ksort($result); $result = array_values($result); // Compare last var of current packet with first var of next packet // On a partial match, remove last var from current packet, // variable header from next packet for ($i = 0, $x = count($result); $i < $x - 1; $i++) { // First packet $fst = substr($result[$i], 0, -1); // Second packet $snd = $result[$i + 1]; // Get last variable from first packet $fstvar = substr($fst, strrpos($fst, "") + 1); // Get first variable from last packet $snd = substr($snd, strpos($snd, "") + 2); $sndvar = substr($snd, 0, strpos($snd, "")); // Check if fstvar is a substring of sndvar // If so, remove it from the first string if (strpos($sndvar, $fstvar) !== false) { $result[$i] = preg_replace("#(\\x00[^\\x00]+\\x00)\$#", "", $result[$i]); } } // Join packets return implode("", $result); }
protected function process_status() { // Make sure we have a valid response if (!$this->hasValidResponse(self::PACKET_STATUS)) { return array(); } // Make buffer for data $buf = new GameQ_Buffer($this->preProcess_status($this->packets_response[self::PACKET_STATUS])); $buf->skip(8); /* skip header */ // Decode the words into an array so we can use this data $words = $this->decodeWords($buf); // Make sure we got OK if (!isset($words[0]) || $words[0] != 'OK') { throw new GameQ_ProtocolsException('Packet Response was not OK! Buffer:' . $buf->getBuffer()); } // Set the result to a new result instance $result = new GameQ_Result(); // Server is always dedicated $result->add('dedicated', TRUE); // No mods, as of yet $result->add('mod', FALSE); // These are the same no matter what mode the server is in $result->add('hostname', $words[1]); $result->add('numplayers', $words[2]); $result->add('maxplayers', $words[3]); $result->add('gametype', $words[4]); $result->add('map', $words[5]); $result->add('roundsplayed', $words[6]); $result->add('roundstotal', $words[7]); // Figure out the number of teams $num_teams = intval($words[8]); // Set the current index $index_current = 9; // Loop for the number of teams found, increment along the way for ($id = 1; $id <= $num_teams; $id++) { $result->addSub('teams', 'tickets', $words[$index_current]); $result->addSub('teams', 'id', $id); // Increment $index_current++; } // Get and set the rest of the data points. $result->add('targetscore', $words[$index_current]); $result->add('online', TRUE); // Forced TRUE, it seems $words[$index_current + 1] is always empty $result->add('ranked', $words[$index_current + 2] === 'true'); $result->add('punkbuster', $words[$index_current + 3] === 'true'); $result->add('password', $words[$index_current + 4] === 'true'); $result->add('uptime', $words[$index_current + 5]); $result->add('roundtime', $words[$index_current + 6]); // The next 3 are empty in MOHWF, kept incase they start to work some day $result->add('ip_port', $words[$index_current + 7]); $result->add('punkbuster_version', $words[$index_current + 8]); $result->add('join_queue', $words[$index_current + 9] === 'true'); $result->add('region', $words[$index_current + 10]); $result->add('pingsite', $words[$index_current + 11]); $result->add('country', $words[$index_current + 12]); unset($buf, $words); return $result->fetch(); }
/** * Overloaded for Killing Floor servername issue, could be all unreal2 games though * * @see GameQ_Protocols_Unreal2::process_details() */ protected function process_details() { // Make sure we have a valid response if (!$this->hasValidResponse(self::PACKET_DETAILS)) { return array(); } // Set the result to a new result instance $result = new GameQ_Result(); // Let's preprocess the rules $data = $this->preProcess_details($this->packets_response[self::PACKET_DETAILS]); // Create a buffer $buf = new GameQ_Buffer($data); $result->add('serverid', $buf->readInt32()); // 0 $result->add('serverip', $buf->readPascalString(1)); // empty $result->add('gameport', $buf->readInt32()); $result->add('queryport', $buf->readInt32()); // 0 // We burn the first char since it is not always correct with the hostname $buf->skip(1); // Read as a regular string since the length is incorrect (what we skipped earlier) $result->add('servername', $buf->readString()); // The rest is read as normal $result->add('mapname', $buf->readPascalString(1)); $result->add('gametype', $buf->readPascalString(1)); $result->add('playercount', $buf->readInt32()); $result->add('maxplayers', $buf->readInt32()); $result->add('ping', $buf->readInt32()); // 0 // @todo: There is extra data after this point (~9 bytes), cant find any reference on what it is unset($buf); // Return the result return $result->fetch(); }
protected function process_players() { // Make sure we have a valid response if (!$this->hasValidResponse(self::PACKET_PLAYERS)) { return array(); } // Set the result to a new result instance $result = new GameQ_Result(); // Make buffer for data $buf = new GameQ_Buffer($this->preProcess_players($this->packets_response[self::PACKET_PLAYERS])); $buf->skip(8); /* skip header */ $words = $this->decodeWords($buf); // Not too important if players are missing. if (!isset($words[0]) || $words[0] != 'OK') { return array(); } // Count the number of words and figure out the highest index. $words_total = count($words) - 1; // The number of player info points $num_tags = $words[1]; // Pull out the tags, they start at index=3, length of num_tags $tags = array_slice($words, 2, $num_tags); // Just incase this changed between calls. $result->add('numplayers', $words[$num_tags + 2]); // Loop until we run out of positions for ($pos = 3 + $num_tags; $pos <= $words_total; $pos += $num_tags) { // Pull out this player $player = array_slice($words, $pos, $num_tags); // Loop the tags and add the proper value for the tag. foreach ($tags as $tag_index => $tag) { $result->addPlayer($tag, $player[$tag_index]); } } // @todo: Add some team definition stuff unset($buf, $tags, $words, $player); return $result->fetch(); }
/** * Handles processing the status data into a usable format * * @throws GameQ_ProtocolsException */ protected function process_status() { // Make sure we have a valid response if (!$this->hasValidResponse(self::PACKET_STATUS)) { return array(); } // Set the result to a new result instance $result = new GameQ_Result(); // Let's preprocess the rules $data = $this->preProcess_status($this->packets_response[self::PACKET_STATUS]); // Create a new buffer $buf = new GameQ_Buffer($data); // Skip the header $buf->skip(6); $result->add('mod', $buf->readPascalString()); $result->add('gametype', $buf->readPascalString()); $result->add('map', $buf->readPascalString()); // Grab the flag $flag = $buf->read(); $bit = 1; foreach (array('dedicated', 'password', 'linux', 'tournament', 'no_alias') as $var) { $value = $flag & $bit ? 1 : 0; $result->add($var, $value); $bit *= 2; } $result->add('num_players', $buf->readInt8()); $result->add('max_players', $buf->readInt8()); $result->add('num_bots', $buf->readInt8()); $result->add('cpu', $buf->readInt16()); $result->add('info', $buf->readPascalString()); $buf->skip(2); // Do teams $num_teams = $buf->read(); $result->add('num_teams', $num_teams); $buf->skip(); for ($i = 0; $i < $num_teams; $i++) { $result->addTeam('name', $buf->readString("\t")); $result->addTeam('score', $buf->readString("\n")); } // Do players // @todo: No code here to do players, no docs either, need example server with players unset($buf, $data); return $result->fetch(); }
/** * Process the server status * * @throws GameQ_ProtocolsException */ protected function process_status() { // Make sure we have a valid response if (!$this->hasValidResponse(self::PACKET_STATUS)) { return array(); } // Set the result to a new result instance $result = new GameQ_Result(); // Lets pre process and make sure these things are in the proper order by id $data = $this->preProcess($this->packets_response[self::PACKET_STATUS]); // Create a new buffer $buf = new GameQ_Buffer($data); // Lets peek and see if the data starts with a \ if ($buf->lookAhead(1) == '\\') { // Burn the first one $buf->skip(1); } // Explode the data $data = explode('\\', $buf->getBuffer()); // Remove the last 2 "items" as it should be final\ array_pop($data); array_pop($data); // Init some vars $num_players = 0; $num_teams = 0; // Now lets loop the array for ($x = 0; $x < count($data); $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); $num_teams++; } else { if (substr($key, 0, $suffix) == 'playername') { $num_players++; } $result->addPlayer(substr($key, 0, $suffix), $val); } } else { $result->add($key, $val); } } // Add the player and team count $result->add('num_players', $num_players); $result->add('num_teams', $num_teams); unset($buf, $data, $key, $val, $suffix, $x); return $result->fetch(); }
/** * Read an Unreal Engine 2 string * * Adapted from original GameQ code * * @param GameQ_Buffer $buf * @return string <string, mixed> */ private function _readUnrealString(GameQ_Buffer &$buf) { // Normal pascal string if (ord($buf->lookAhead(1)) < 129) { return $buf->readPascalString(1); } // UnrealEngine2 color-coded string $length = ($buf->readInt8() - 128) * 2 - 3; $encstr = $buf->read($length); $buf->skip(3); // Remove color-code tags $encstr = preg_replace('~\\x5e\\0\\x23\\0..~s', '', $encstr); // Remove every second character // The string is UCS-2, this approximates converting to latin-1 $str = ''; for ($i = 0, $ii = strlen($encstr); $i < $ii; $i += 2) { $str .= $encstr[$i]; } return $str; }
protected function process_players() { // Make sure we have a valid response if (!$this->hasValidResponse(self::PACKET_PLAYERS)) { return array(); } // Set the result to a new result instance $result = new GameQ_Result(); // Make buffer for data $buf = new GameQ_Buffer($this->preProcess_players($this->packets_response[self::PACKET_PLAYERS])); $buf->skip(8); /* skip header */ $words = $this->decodeWords($buf); // Not too important if players are missing. if (!isset($words[0]) || $words[0] != 'OK') { return array(); } // The number of player info points $num_tags = $words[1]; $position = 2; $tags = array(); for (; $position < $num_tags + 2; $position++) { $tags[] = $words[$position]; } $num_players = $words[$position]; $position++; $start_position = $position; for (; $position < $num_players * $num_tags + $start_position; $position += $num_tags) { for ($a = $position, $b = 0; $a < $position + $num_tags; $a++, $b++) { $result->addPlayer($tags[$b], $words[$a]); } } // @todo: Add some team definition stuff unset($buf); return $result->fetch(); }