I'm trying to write a PHP TCP client to connect to an Unreal Engine 5 RCON server. I'm using an existing C# project on GitHub as a reference but I'm having trouble preparing packets to send. The GitHub project prepares a byte
array which is then sent to the server. I've tried to re-write the function in PHP but I'm not sure I've done it correctly. But the bigger problem is socket_write()
socket_send()
and fwrite()
all want a string
and I now have an array
. I believe I can format my array as a string to represent the bytes using pack()
but I'm not sure how.
Here is the C# function from https://github.com/Sp1rit/ArkRcon/blob/master/Rcon/RconPacket.cs So this shows what I need to send.
public static implicit operator byte[](RconPacket packet)
{
byte[] buffer = new byte[packet.Size + 4];
packet.Size.ToLittleEndian().CopyTo(buffer, 0);
packet.Id.ToLittleEndian().CopyTo(buffer, 4);
((int)packet.Type).ToLittleEndian().CopyTo(buffer, 8);
Encoding.ASCII.GetBytes(packet.Body).CopyTo(buffer, 12);
return buffer;
}
Here is my attempt at converting that (See ConvertToArray()
below). I did use __invoke
but as there's also a reverse function and it wouldn't let me use __invoke
twice. I've provided the whole class so you can see the full picture and you can see some commented code where I've tried different things.
class RconPacket
{
private static int $idCounter = 1;
public $Id;
public int $Type;
public $Body;
public function GetSize()
{
return (!empty($Body) ? strlen($Body) + 10 : 0);
}
public function __construct($type, $body)
{
$this->Id = RconPacket::$idCounter++;
$this->Type = $type;
$this->Body = $body;
}
public function ConvertToArray(): array
{
$buffer = array(); //array_fill(0, $this->GetSize() + 4, 0);
$buffer[] = pack('V', $this->GetSize() + 4); //->copy($buffer, 0);
$buffer[] = pack('V', $this->Id); //->copy($buffer, 4);
$buffer[] = pack('V', (int)$this->Type); //->copy($buffer, 8);
$buffer = array_merge($buffer, array_map(fn($c) => ord($c), str_split($this->Body)));
//$buffer[] = str_split($this->Body)->map(fn($c, $i) => ord($c)); //->copy($buffer, 12);
return $buffer;
}
public static function ConvertToRconPacket(string $data): RconPacket
{
$size = unpack('V', $data)[1];
$id = unpack('V', $data, 4)[1];
$type = unpack('V', $data, 8)[1];
$body = substr($data, 12, $size - 10);
return new RconPacket($id, $type, $body);
}
}
I've tried converting the array to a string using pack("a", $buffer);
but pack()
also requires a string
A basic knowledge: string
in PHP is actually a binary array (e.g. $str[3]
is the 4th byte), so you don't have to use a PHP array, you can just use the string packed by the pack
function.
$data = pack('V3a*',
$this->GetSize(),
$this->Id,
(int)$this->Type,
$this->Body);
fwrite($fp, $data);