EngineGP/system/library/games/query/SourceQuery.php

331 lines
9.2 KiB
PHP
Raw Normal View History

2023-03-04 23:45:46 +00:00
<?php
2023-05-05 01:17:19 +00:00
if (!DEFINED('EGP'))
exit(header('Refresh: 0; URL=http://' . $_SERVER['SERVER_NAME'] . '/404'));
class SourceQuery
{
const GOLDSOURCE = 0;
const SOURCE = 1;
const A2S_PING = 0x69;
const A2S_INFO = 0x54;
const A2S_PLAYER = 0x55;
const A2S_RULES = 0x56;
const A2S_SERVERQUERY_GETCHALLENGE = 0x57;
const S2A_PING = 0x6A;
const S2A_CHALLENGE = 0x41;
const S2A_INFO = 0x49;
const S2A_INFO_OLD = 0x6D;
const S2A_PLAYER = 0x44;
const S2A_RULES = 0x45;
const S2A_RCON = 0x6C;
const SERVERDATA_EXECCOMMAND = 2;
const SERVERDATA_AUTH = 3;
const SERVERDATA_RESPONSE_VALUE = 0;
const SERVERDATA_AUTH_RESPONSE = 2;
private $Rcon;
private $Socket;
private $Connected;
private $Challenge;
private $UseOldGetChallengeMethod;
public function __construct(BaseSocket $Socket = null)
2023-03-05 13:59:34 +00:00
{
2023-05-05 01:17:19 +00:00
$this->Socket = $Socket ?: new Socket();
}
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
public function __destruct()
{
$this->Disconnect();
}
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
public function Connect($Address, $Port, $Timeout = 3, $Engine = self::SOURCE)
{
$this->Disconnect();
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
if (!is_int($Timeout) || $Timeout < 0)
return false;
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
$this->Socket->Open($Address, (int)$Port, $Timeout, (int)$Engine);
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
$this->Connected = true;
}
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
public function SetUseOldGetChallengeMethod($Value)
{
$Previous = $this->UseOldGetChallengeMethod;
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
$this->UseOldGetChallengeMethod = $Value === true;
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
return $Previous;
}
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
public function Disconnect()
{
$this->Connected = false;
$this->Challenge = 0;
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
$this->Socket->Close();
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
if ($this->Rcon) {
$this->Rcon->Close();
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
$this->Rcon = null;
2023-03-05 13:59:34 +00:00
}
2023-05-05 01:17:19 +00:00
}
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
public function Ping()
{
if (!$this->Connected)
return false;
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
$this->Socket->Write(self::A2S_PING);
$Buffer = $this->Socket->Read();
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
return $Buffer->GetByte() === self::S2A_PING;
}
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
public function GetInfo()
{
if (!$this->Connected)
return false;
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
$this->Socket->Write(self::A2S_INFO, "Source Engine Query\0");
$Buffer = $this->Socket->Read();
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
$Type = $Buffer->GetByte();
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
if ($Type === self::S2A_INFO_OLD && $this->Socket->Engine === self::GOLDSOURCE) {
$Server['Address'] = $Buffer->GetString();
2023-03-05 13:59:34 +00:00
$Server['HostName'] = $Buffer->GetString();
$Server['Map'] = $Buffer->GetString();
$Server['ModDir'] = $Buffer->GetString();
$Server['ModDesc'] = $Buffer->GetString();
$Server['Players'] = $Buffer->GetByte();
$Server['MaxPlayers'] = $Buffer->GetByte();
2023-05-05 01:17:19 +00:00
$Server['Protocol'] = $Buffer->GetByte();
2023-03-05 13:59:34 +00:00
$Server['Dedicated'] = Chr($Buffer->GetByte());
$Server['Os'] = Chr($Buffer->GetByte());
$Server['Password'] = $Buffer->GetByte() === 1;
2023-05-05 01:17:19 +00:00
$Server['IsMod'] = $Buffer->GetByte() === 1;
if ($Server['IsMod']) {
$Mod['Url'] = $Buffer->GetString();
$Mod['Download'] = $Buffer->GetString();
$Buffer->Get(1);
$Mod['Version'] = $Buffer->GetLong();
$Mod['Size'] = $Buffer->GetLong();
$Mod['ServerSide'] = $Buffer->GetByte() === 1;
$Mod['CustomDLL'] = $Buffer->GetByte() === 1;
2023-03-05 13:59:34 +00:00
}
2023-05-05 01:17:19 +00:00
$Server['Secure'] = $Buffer->GetByte() === 1;
$Server['Bots'] = $Buffer->GetByte();
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
if (isset($Mod))
$Server['Mod'] = $Mod;
2023-03-05 13:59:34 +00:00
return $Server;
}
2023-05-05 01:17:19 +00:00
if ($Type !== self::S2A_INFO)
return false;
if ($Type !== self::S2A_INFO)
return false;
$Server['Protocol'] = $Buffer->GetByte();
$Server['HostName'] = $Buffer->GetString();
$Server['Map'] = $Buffer->GetString();
$Server['ModDir'] = $Buffer->GetString();
$Server['ModDesc'] = $Buffer->GetString();
$Server['AppID'] = $Buffer->GetShort();
$Server['Players'] = $Buffer->GetByte();
$Server['MaxPlayers'] = $Buffer->GetByte();
$Server['Bots'] = $Buffer->GetByte();
$Server['Dedicated'] = Chr($Buffer->GetByte());
$Server['Os'] = Chr($Buffer->GetByte());
$Server['Password'] = $Buffer->GetByte() === 1;
$Server['Secure'] = $Buffer->GetByte() === 1;
if ($Server['AppID'] === 2400) {
$Server['GameMode'] = $Buffer->GetByte();
$Server['WitnessCount'] = $Buffer->GetByte();
$Server['WitnessTime'] = $Buffer->GetByte();
}
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
$Server['Version'] = $Buffer->GetString();
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
if ($Buffer->Remaining() > 0) {
$Server['ExtraDataFlags'] = $Flags = $Buffer->GetByte();
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
if ($Flags & 0x80)
$Server['GamePort'] = $Buffer->GetShort();
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
if ($Flags & 0x10) {
$SteamIDLower = $Buffer->GetUnsignedLong();
$SteamIDInstance = $Buffer->GetUnsignedLong();
$SteamID = 0;
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
if (PHP_INT_SIZE === 4) {
if (extension_loaded('gmp')) {
$SteamIDLower = gmp_abs($SteamIDLower);
$SteamIDInstance = gmp_abs($SteamIDInstance);
$SteamID = gmp_strval(gmp_or($SteamIDLower, gmp_mul($SteamIDInstance, gmp_pow(2, 32))));
} else
return false;
} else
$SteamID = $SteamIDLower | ($SteamIDInstance << 32);
$Server['SteamID'] = $SteamID;
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
unset($SteamIDLower, $SteamIDInstance, $SteamID);
2023-03-05 13:59:34 +00:00
}
2023-05-05 01:17:19 +00:00
if ($Flags & 0x40) {
$Server['SpecPort'] = $Buffer->GetShort();
$Server['SpecName'] = $Buffer->GetString();
}
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
if ($Flags & 0x20)
$Server['GameTags'] = $Buffer->GetString();
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
if ($Flags & 0x01)
$Server['GameID'] = $Buffer->GetUnsignedLong() | ($Buffer->GetUnsignedLong() << 32);
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
if ($Buffer->Remaining() > 0)
2023-03-05 13:59:34 +00:00
return false;
2023-05-05 01:17:19 +00:00
}
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
return $Server;
}
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
public function GetPlayers()
{
if (!$this->Connected)
return false;
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
$this->GetChallenge(self::A2S_PLAYER, self::S2A_PLAYER);
$this->Socket->Write(self::A2S_PLAYER, $this->Challenge);
$Buffer = $this->Socket->Read(14000);
$Type = $Buffer->GetByte();
if ($Type !== self::S2A_PLAYER)
return false;
$Players = [];
$Count = $Buffer->GetByte();
while ($Count-- > 0 && $Buffer->Remaining() > 0) {
$Player['Id'] = $Buffer->GetByte();
$Player['Name'] = $Buffer->GetString();
$Player['Frags'] = $Buffer->GetLong();
$Player['Time'] = (int)$Buffer->GetFloat();
$Player['TimeF'] = GMDate(($Player['Time'] > 3600 ? "H:i:s" : "i:s"), $Player['Time']);
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
$Players[] = $Player;
2023-03-05 13:59:34 +00:00
}
2023-05-05 01:17:19 +00:00
return $Players;
}
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
public function GetRules()
{
if (!$this->Connected)
return false;
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
$this->GetChallenge(self::A2S_RULES, self::S2A_RULES);
$this->Socket->Write(self::A2S_RULES, $this->Challenge);
$Buffer = $this->Socket->Read();
$Type = $Buffer->GetByte();
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
if ($Type !== self::S2A_RULES)
return false;
$Rules = [];
$Count = $Buffer->GetShort();
while ($Count-- > 0 && $Buffer->Remaining() > 0) {
$Rule = $Buffer->GetString();
$Value = $Buffer->GetString();
if (!empty($Rule))
$Rules[$Rule] = $Value;
2023-03-05 13:59:34 +00:00
}
2023-05-05 01:17:19 +00:00
return $Rules;
}
private function GetChallenge($Header, $ExpectedResult)
{
if ($this->Challenge)
return;
if ($this->UseOldGetChallengeMethod)
$Header = self::A2S_SERVERQUERY_GETCHALLENGE;
$this->Socket->Write($Header, "\xFF\xFF\xFF\xFF");
$Buffer = $this->Socket->Read();
$Type = $Buffer->GetByte();
switch ($Type) {
case self::S2A_CHALLENGE:
2023-03-05 13:59:34 +00:00
{
2023-05-05 01:17:19 +00:00
$this->Challenge = $Buffer->Get(4);
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
return;
}
case $ExpectedResult:
{
return;
}
case 0:
{
return;
}
default:
2023-03-05 13:59:34 +00:00
{
2023-05-05 01:17:19 +00:00
return;
2023-03-05 13:59:34 +00:00
}
2023-05-05 01:17:19 +00:00
}
}
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
public function SetRconPassword($Password)
{
if (!$this->Connected) {
return false;
2023-03-05 13:59:34 +00:00
}
2023-05-05 01:17:19 +00:00
switch ($this->Socket->Engine) {
case SourceQuery::GOLDSOURCE:
2023-03-05 13:59:34 +00:00
{
2023-05-05 01:17:19 +00:00
$this->Rcon = new GoldSourceRcon($this->Socket);
break;
2023-03-05 13:59:34 +00:00
}
2023-05-05 01:17:19 +00:00
case SourceQuery::SOURCE:
2023-03-05 13:59:34 +00:00
{
2023-05-05 01:17:19 +00:00
$this->Rcon = new SourceRcon($this->Socket);
break;
2023-03-05 13:59:34 +00:00
}
2023-05-05 01:17:19 +00:00
}
2023-03-05 13:59:34 +00:00
2023-05-05 01:17:19 +00:00
$this->Rcon->Open();
$this->Rcon->Authorize($Password);
}
public function Rcon($Command)
{
if (!$this->Connected) {
return false;
2023-03-05 13:59:34 +00:00
}
2023-05-05 01:17:19 +00:00
if ($this->Rcon === null) {
return false;
}
return $this->Rcon->Command($Command);
}
}