Hi,

Sorry for the confusing title, as will become clear shortly this is completely unfamiliar territory for me, so I have really no clue what I'm doing.


Anyway, I am trying to interact with a game server for Call of Duty: Black Ops, this is usually called 'remote console'. I had no idea where to start so I googled around a bit and found a complete web-based remote console implementation (I did not try it but I am assuming it works). Unfortunately, it is in PHP, about which I know absolutely nothing.

The most important file in the PHP implementation, I think, is this:
php Code:
  1. <?php defined('SYSPATH') or die('No direct script access.');
  2. /**
  3.  * Remote console connection
  4.  *
  5.  * @author      EpicLegion
  6.  * @package     rcon
  7.  * @subpackage  lib
  8.  */
  9. class Rcon {
  10.  
  11.     /**
  12.      * Socket res
  13.      *
  14.      * @var resource
  15.      */
  16.     protected $socket = NULL;
  17.  
  18.     /**
  19.      * Password
  20.      *
  21.      * @var string
  22.      */
  23.     protected $password = '';
  24.  
  25.     /**
  26.      * Host (IP)
  27.      *
  28.      * @var string
  29.      */
  30.     protected $host = '';
  31.  
  32.     /**
  33.      * Serve port
  34.      * @var int
  35.      */
  36.     protected $port = 3074;
  37.  
  38.     /**
  39.      * Constructor
  40.      *
  41.      * @param   string  $host
  42.      * @param   int     $port
  43.      * @param   string  $password
  44.      */
  45.     public function __construct($host = '', $port = 3074, $password = '')
  46.     {
  47.         $this->host = $host;
  48.         $this->port = $port;
  49.         $this->password = $password;
  50.     }
  51.  
  52.     /**
  53.      * Send command
  54.      *
  55.      * @param   string      $command
  56.      * @param   bool        $return_response
  57.      * @throws  Exception
  58.      * @return  mixed
  59.      */
  60.     public function command($command, $return_response = TRUE)
  61.     {
  62.         // No connection
  63.         if(!$this->socket OR !is_resource($this->socket))
  64.         {
  65.             throw new Exception('You must connect to remote console before issuing commands');
  66.         }
  67.  
  68.         // Send command
  69.         fwrite($this->socket, "\xff\xff\xff\xff\x00".$this->password.' '.$command."\x00");
  70.  
  71.         // Wait for response?
  72.         if($return_response)
  73.         {
  74.             // Retrieve reponse
  75.             $response = $this->get_response();
  76.  
  77.             // Invalid password?
  78.             if(trim($response) == 'Invalid password.')
  79.             {
  80.                 throw new Exception('Invalid remote console password.');
  81.             }
  82.  
  83.             return $response;
  84.         }
  85.     }
  86.  
  87.     /**
  88.      * Connect to rcon
  89.      *
  90.      * @throws  Exception
  91.      */
  92.     public function connect()
  93.     {
  94.         // Errors
  95.         $errno = 0;
  96.         $errstr = '';
  97.  
  98.         // Open socket
  99.         $this->socket = fsockopen('udp://'.$this->host, $this->port, $errno, $errstr, 5);
  100.  
  101.         // Invalid?
  102.         if(!$this->socket)
  103.         {
  104.             throw new Exception('Cannot connect to remote console');
  105.         }
  106.     }
  107.  
  108.     /**
  109.      * Disconnect
  110.      */
  111.     public function disconnect()
  112.     {
  113.         fclose($this->socket);
  114.     }
  115.  
  116.     /**
  117.      * Read response
  118.      *
  119.      * @return  string
  120.      */
  121.     public function get_response()
  122.     {
  123.         // Var containing response
  124.         $response = '';
  125.  
  126.         // Set socket timeout
  127.         stream_set_timeout($this->socket, 0, 7e5);
  128.  
  129.         // Lol how rare
  130.         do
  131.         {
  132.             // Read 8kb
  133.             $stream_read = fread($this->socket, 8192);
  134.  
  135.             // End of response?
  136.             if(strpos($stream_read, "\x00") !== FALSE)
  137.             {
  138.                 // Cut
  139.                 $stream_read = substr($stream_read, 0, strpos($stream_read, "\x00"));
  140.             }
  141.  
  142.             // Get socket info
  143.             $stream_info = stream_get_meta_data($this->socket);
  144.  
  145.             // Append
  146.             $response .= substr(trim($stream_read, "\x0a"), 11);
  147.         }
  148.         while(!$stream_info['timed_out']);
  149.  
  150.         // Return
  151.         return $response;
  152.     }
  153.  
  154.     /**
  155.      * Get socket resource
  156.      *
  157.      * @return  resource
  158.      */
  159.     public function get_socket()
  160.     {
  161.         return $this->socket;
  162.     }
  163.  
  164.     /**
  165.      * Set RCON password
  166.      *
  167.      * @param   string  $password
  168.      */
  169.     public function set_password($password = '')
  170.     {
  171.         $this->password = $password;
  172.     }
  173.  
  174.     /**
  175.      * Set server connection details
  176.      *
  177.      * @param   string  $host
  178.      * @param   int     $port
  179.      */
  180.     public function set_server_info($host = '', $port = 3074)
  181.     {
  182.         $this->host = $host;
  183.         $this->port = $port;
  184.     }
  185. }

I can logically split this file into three parts:
  1. 'Properties' like Host, Port and Password, including setters / getters / constructor, etc. I can manage this obviously.
  2. Connecting and sending commands to the host, as well as receiving a response. This is what I'm having trouble with.
  3. Parsing the response into readable chunks. I am not here yet (though I might need some help with that too), I'd rather get item 2 working first.



So, I looked around a bit and found the UdpClient class. The command to connect in the PHP source uses "udp:// ..." so I thought that would be a good place to start.


I wrote this class as a translation of the PHP code, at least the connecting and sending a command part:
csharp Code:
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Net;
  5. using System.Net.Sockets;
  6. using System.Text;
  7. using System.Threading;
  8.  
  9. namespace BlackOpsRconTest
  10. {
  11.     public class Rcon
  12.     {
  13.         public Rcon(string host, string password)
  14.         {
  15.             this.Host = host;
  16.             this.Port = 3074; // I don't think this can ever change for this game
  17.             this.Password = password;
  18.  
  19.             client = new UdpClient();
  20.         }
  21.  
  22.         public string Host { get; set; }
  23.         public int Port { get; set; }
  24.         public string Password { get; set; }
  25.  
  26.         private readonly UdpClient client;
  27.  
  28.         private static bool messageReceived = false;
  29.  
  30.         public void Connect()
  31.         {
  32.             client.Connect(this.Host, this.Port);
  33.         }
  34.  
  35.         public void Disconnect()
  36.         {
  37.             client.Close();
  38.         }
  39.        
  40.         public void SendCommand(string command)
  41.         {
  42.             // preCommand is supposed to be the '\xff\xff\xff\xff\x00' stuff...
  43.             string preCommand = new String(Convert.ToChar(0xff), 4);
  44.             preCommand += Convert.ToChar(0x00);
  45.  
  46.             // get the bytes to send
  47.             // this spells     \xff\xff\xff\xff\x00<password> <command>\x00
  48.             // as in the PHP source...
  49.             var commandBytes = Encoding.ASCII.GetBytes(String.Format("{0}{1} {2}{3}",
  50.                                                                      preCommand,
  51.                                                                      this.Password,
  52.                                                                      command,
  53.                                                                      Convert.ToChar(0x00)));
  54.             // Send it!
  55.             client.Send(commandBytes, commandBytes.Length);
  56.  
  57.  
  58.             // Now listen for response.
  59.             IPEndPoint e = new IPEndPoint(IPAddress.Any, this.Port);
  60.             UdpState s = new UdpState();
  61.             s.e = e;
  62.             s.u = client;
  63.  
  64.             Console.WriteLine("listening for messages");
  65.             client.BeginReceive(new AsyncCallback(ReceiveCallback), s);
  66.  
  67.             // Do some work while we wait for a message. For this example,
  68.             // we'll just sleep
  69.             while (!messageReceived)
  70.             {
  71.                 Thread.Sleep(100);
  72.             }
  73.         }
  74.  
  75.         private static void ReceiveCallback(IAsyncResult ar)
  76.         {
  77.             UdpClient u = (UdpClient)((UdpState)(ar.AsyncState)).u;
  78.             IPEndPoint e = (IPEndPoint)((UdpState)(ar.AsyncState)).e;
  79.  
  80.             Byte[] receiveBytes = u.EndReceive(ar, ref e);
  81.             string receiveString = Encoding.ASCII.GetString(receiveBytes);
  82.  
  83.             Console.WriteLine("Received: {0}", receiveString);
  84.             messageReceived = true;
  85.         }
  86.  
  87.         struct UdpState
  88.         {
  89.             public UdpClient u;
  90.             public IPEndPoint e;
  91.         }
  92.  
  93.     }
  94. }
The sending part is mostly 'made up', I assumed that the \xff and \x00 stuff was hex so I used Convert.ToChar to convert those hex numbers to characters and insert them before the actual command to send, just like the PHP source code does (at least: i think it does that, I may be completely wrong here as I have no clue how PHP works).

The receiving part is straight from MSDN.

I am trying to use it as such:
csharp Code:
  1. Rcon r = new Rcon("173.199.111.167", "mypassword");
  2.             r.Connect();
  3.             r.SendCommand("teamstatus");
"teamstatus" is supposed to be a valid command that should result in the server returning the players and their status (name, id, score, ping, etc).


When I try to run this, nothing happens. The console displays "listening for messages" but nothing after that. I waited a few minutes but no response ever came.


What am I doing wrong? As I said, I am completely new to this (both the PHP and to 'socket' stuff in general) so I really have no clue what I'm doing. At this point basically I'm trying to hack together a translation and I'm just looking to get A response, no matter what.

Thanks!