I. Introduction▲
On peut facilement récupérer toutes sortes d'informations en envoyant des paquets TCP ou UDP prédéfinis à des servers. Ceux-ci nous renverront un ou plusieurs paquets, appelés réponse, qui contiendront l'information désirée.
Les paquets utilisés par Steam ont une taille de 1400 bytes plus les entêtes IP/UDP (ou de 4096 bytes pour les commandes RCON).
Les réponses obtenues par le serveur sont généralement peu lisibles. Pour les rendre plus lisibles, j'ai écrit à la fin de cet article une série de fonctions qui permettent de convertir les réponses.
I-A. À lire▲
Chaque sous-partie va être séparée comme suit :
- Format de la requête correspond à la commande qu'il faut envoyer au serveur pour récupérer la réponse appropriée ;
- Format de la réponse explique les différentes parties qui composent la réponse. ;
- Script nous montre le code PHP utilisé.
II. Requête sur un serveur▲
Un serveur répond à quatre requêtes qui sont les suivantes :
- Récupérer un numéro de défi (challenge number) ;
- Récupérer des informations à propos du serveur ;
- Récupérer des informations sur les joueurs présents sur le serveur ;
- Récupérer des informations sur les règles du serveur.
Les requêtes doivent se faire via le protocole UDP.
II-A. Challenge number▲
Le challenge number est requis pour les requêtes concernant la récupération des règles du serveur ainsi que des joueurs présents dessus.
II-A-1. Format de la requête▲
0020
ff ff ff ff 57
....
W
Donnée |
Type |
---|---|
0xFFFFFF |
int |
W ou 0x57 |
byte |
II-A-2. Format de la réponse▲
0020
ff ff ff ff 41
fa 05
a4 00
....
A....
Donnée |
Type |
Explication |
---|---|---|
0xFFFFFF |
int |
- |
Challenge |
long |
Le « challenge number » à utiliser |
II-A-3. Script▲
// Constant
define('
PACKET_SIZE
'
,
'
1400
'
);
define('
SERVERQUERY_GETCHALLENGE
'
,
"
\xFF\xFF\xFF\xFF\x57
"
);
define ('
REPLY_GETCHALLENGE
'
,
"
\x41
"
);
// Ip address and port
$_ip
=
'
82.149.249.243
'
;
$_port
=
'
27017
'
;
// Open a connection with server
$socket
=
stream_socket_client('
udp://
'
.
$_ip
.
'
:
'
.
$_port
,
$errno
,
$errstr
,
30
);
// Send command to server
$cmd
=
SERVERQUERY_GETCHALLENGE;
$length
=
strlen($cmd
);
fwrite($socket
,
$cmd
,
$length
);
// Get response from server
$response
=
fread($socket
,
PACKET_SIZE);
// Clean response
$pattern
=
"
#
\xFF\xFF\xFF\xFF
"
.
REPLY_GETCHALLENGE.
"
#
"
;
$response
=
preg_replace($pattern
,
''
,
$response
);
II-B. Informations sur le serveur▲
Cette partie consiste à récupérer toutes les informations disponibles sur un serveur telles que son nom, nombre de joueurs maximum, nombre de joueurs présents, carte en cours …
II-B-1. Format de la requête▲
0020
FF FF FF FF 54
53
6
F 75
72
63
65
20
45
6
E 67
69
&
#255;ÿÿÿTSource Engi
0030
6
E 65
20
51
75
65
72
79
00
ne Query
Donnée |
Type |
---|---|
0xFFFFFF |
int |
T |
byte |
Source Engine Query |
string |
II-B-2. Format de la réponse▲
0020
ff ff ff ff 49
07
....
I.
0030
77
77
77
2
e 63
6
c 61
6
e 6
b 69
6
c 6
c 65
72
7
a 2
e www.
clankillerz.
0040
64
65
20
2
e 2
e 3
a 3
a 50
75
62
6
c 69
63
20
6
f 6
e de ..::
Public on
0050
6
c 79
20
48
69
67
68
73
6
b 69
6
c 6
c 3
a 3
a 2
e 2
e ly Highskill::..
0060
00
64
65
5
f 64
75
73
74
32
00
63
73
74
72
69
6
b .
de_dust2.
cstrik
0070
65
00
43
6
f 75
6
e 74
65
72
2
d 53
74
72
69
6
b 65
e.
Counter-
Strike
0080
3
a 20
53
6
f 75
72
63
65
00
f0 00
00
0
f 00
64
77
:
Source......
dw
0090
00
01
31
2
e 30
2
e 30
2
e 33
34
00
.
.1.0.0.34
.
Donnée |
Type |
Explication |
---|---|---|
0xFFFFFF |
int |
- |
Type |
byte |
Type est toujours égal à 'I' (0x49) |
Version |
byte |
Version du réseau Steam, actuellement 0x07 |
Nom du server |
string |
- |
Carte |
string |
- |
Répertoire du jeu |
string |
Le nom du répertoire qui contient les fichiers du jeu (ex. : « cstrike ») |
Description du jeu |
string |
Le nom complet du jeu (ex. : « Counter-Strike : Source ») |
AppID |
short |
Steam Application ID, pour CSS c'est 240 |
Nombre de joueurs |
byte |
- |
Nombre maximum de joueurs |
byte |
- |
Nombre de bots |
byte |
- |
Serveur dédié |
byte |
'l' pour listen, 'd' pour dedicated et 'p' pour Source TV |
OS |
byte |
Système d'exploitation ; 'l' pour Linux et 'w' pour Windows |
Mot de passe |
byte |
Indique si le serveur est verrouillé par mot de passe ; si 0x01 il y a un mot de passe |
Sécurisé |
byte |
Indique si VAC est activé ; si 0x01 VAC est activé |
Version du jeu |
string |
Version du jeu (ex. : « 1.0.0.34 ») |
II-B-3. Script▲
// Constant
define('
PACKET_SIZE
'
,
'
1400
'
);
define('
SERVERQUERY_INFO
'
,
"
\xFF\xFF\xFF\xFF
TSource Engine Query
"
);
define ('
REPLY_INFO
'
,
"
\x49
"
);
// Ip address and port
$_ip
=
'
82.149.249.243
'
;
$_port
=
'
27017
'
;
// Open connection with server
$socket
=
stream_socket_client('
udp://
'
.
$_ip
.
'
:
'
.
$_port
,
$errno
,
$errstr
,
30
);
// Send command to server
$cmd
=
SERVERQUERY_INFO;
$length
=
strlen($cmd
);
fwrite($socket
,
$cmd
,
$length
);
// Get response from server
$response
=
fread($socket
,
PACKET_SIZE);
// Clean response
$pattern
=
"
#
\xFF\xFF\xFF\xFF
"
.
REPLY_INFO.
"
#
"
;
$response
=
preg_replace($pattern
,
''
,
$response
);
//Version - byte (Network version. 0x07 is the current Steam version.)
$server
[
'
version
'
]
=
getByte($response
);
// Ip and port
$server
[
'
ip
'
]
=
$_ip
;
$server
[
'
port
'
]
=
$_port
;
//Server Name - string (The Source server's name, eg: "Recoil NZ CS Server #1")
$server
[
'
name
'
]
=
trim(getString($response
));
//Map - string (The current map being played, eg: "de_dust")
$server
[
'
map
'
]
=
getString($response
);
//Game Directory - string (The name of the folder containing the game files, eg: "cstrike")
$server
[
'
gamedir
'
]
=
getString($response
);
//Game Description- string (A friendly string name for the game type, eg: "Counter-Strike: Source")
$server
[
'
gamedesc
'
]
=
getString($response
);
//AppID - short (Steam Application ID)
$server
[
'
appid
'
]
=
getShortSigned($response
);
//Number of players - byte (The number of players currently on the server)
$server
[
'
numplayers
'
]
=
getByte($response
);
//Maximum players - byte (Maximum allowed players for the server)
$server
[
'
maxplayers
'
]
=
getByte($response
);
//Number of bots - byte (Number of bot players currently on the server)
$server
[
'
bot
'
]
=
getByte($response
);
//Dedicated - byte ('l' for listen, 'd' for dedicated, 'p' for SourceTV)
$data
=
chr(getByte($response
));
$server
[
'
dedicated
'
]
=
0
;
$server
[
'
sourcetv
'
]
=
0
;
$server
[
'
listen
'
]
=
0
;
if ($data
==
'
d
'
) $server
[
'
dedicated
'
]
=
1
;
if ($data
==
'
p
'
) $server
[
'
sourcetv
'
]
=
1
;
if ($data
==
'
l
'
) $server
[
'
listen
'
]
=
1
;
//OS - byte (Host operating system. 'l' for Linux, 'w' for Windows)
$data
=
chr(getByte($response
));
$server
[
'
os
'
]
=
'
undefined
'
;
if ($data
==
'
l
'
)
{
$server
[
'
os
'
]
=
'
linux
'
;
}
elseif ($data
==
'
w
'
)
{
$server
[
'
os
'
]
=
'
windows
'
;
}
//Password - byte (If set to 0x01, a password is required to join this server)
$data
=
getByte($response
);
$server
[
'
password
'
]
=
0
;
if ($data
==
1
)
{
$server
[
'
password
'
]
=
1
;
}
//Secure - byte (if set to 0x01, this server is VAC secured)
$data
=
getByte($response
);
$server
[
'
secure
'
]
=
1
;
if ($data
==
1
)
{
$server
[
'
secure
'
]
=
1
;
}
//Game Version - string (The version of the game, eg: "1.0.0.22")
$server
[
'
gameversion
'
]
=
getString($response
);
L'affichage du tableau $server nous donne :
Array
(
[
version]
=>
7
[
ip]
=>
82.149.249.243
[
port]
=>
27017
[
name]
=>
www.
clankillerz.
de ..::
Public only Highskill::..
[
map]
=>
de_dust2
[
gamedir]
=>
cstrike
[
gamedesc]
=>
Counter-
Strike:
Source
[
appid]
=>
240
[
numplayers]
=>
0
[
maxplayers]
=>
15
[
bot]
=>
0
[
dedicated]
=>
1
[
sourcetv]
=>
0
[
listen]
=>
0
[
os]
=>
windows
[
password]
=>
0
[
secure]
=>
1
[
gameversion]
=>
1.0.0.34
)
II-C. Informations sur les règles du serveur▲
Cette partie consiste à récupérer toutes les règles sur un serveur telles que mp_timelimit, sv_alltalk…
II-C-1. Format de la requête▲
FF FF FF FF 56
<
4
byte challenge number>
....
V
Donnée |
Type |
---|---|
0xFFFFFF |
int |
V (ou 0x56) |
byte |
Challenge Number |
long |
II-C-2. Format de la réponse▲
0020
ff ff ff ff 45
41
....
EA
0030
00
6
d 61
74
74
69
65
5
f 6
d 75
67
6
d 6
f 64
00
31
.
mattie_mugmod.
1
0040
00
6
d 61
74
74
69
65
5
f 65
76
65
6
e 74
73
63
72
.
mattie_eventscr
0050
69
70
74
73
00
31
00
65
76
65
6
e 74
73
63
72
69
ipts.
1.
eventscri
0060
70
74
73
5
f 76
65
72
00
31
2
e 33
2
e 30
2
e 30
30
pts_ver.
1.3.0.00
0070
36
00
6
d 61
6
e 69
5
f 72
65
73
65
72
76
65
5
f 73
6.
mani_reserve_s
0080
6
c 6
f 74
73
00
30
00
6
d 61
6
e 69
5
f 61
64
6
d 69
lots.
0.
mani_admi
0090
6
e 5
f 70
6
c 75
67
69
6
e 5
f 76
65
72
73
69
6
f 6
e n_plugin_version
00
a0 00
31
2
e 32
42
65
74
61
52
20
56
53
50
00
6
d 61
.1.2
BetaR VSP.
ma
00
b0 6
e 69
5
f 74
69
63
6
b 72
61
74
65
00
31
30
30
00
ni_tickrate.
100.
00
c0 6
d 61
6
e 69
5
f 6
e 65
78
74
6
d 61
70
00
63
73
5
f mani_nextmap.
cs_
00
d0 6
f 66
66
69
63
65
00
65
73
74
5
f 76
65
72
73
69
office.
est_versi
00e0
6
f 6
e 00
30
2
e 34
31
37
61
00
6
d 70
5
f 74
65
61
on.
0.417
a.
mp_tea
00
f0 6
d 70
6
c 61
79
00
30
00
6
d 70
5
f 66
72
61
67
6
c mplay.
0.
mp_fragl
0100
69
6
d 69
74
00
30
00
6
d 70
5
f 66
61
6
c 6
c 64
61
imit.
0.
mp_fallda
0110
6
d 61
67
65
00
31
00
6
d 70
5
f 77
65
61
70
6
f 6
e mage.
1.
mp_weapon
0120
73
74
61
79
00
30
00
6
d 70
5
f 66
6
f 72
63
65
72
stay.
0.
mp_forcer
0130
65
73
70
61
77
6
e 00
31
00
6
d 70
5
f 66
6
f 6
f 74
espawn.
1.
mp_foot
0140
73
74
65
70
73
00
31
00
6
d 70
5
f 66
6
c 61
73
68
steps.
1.
mp_flash
0150
6
c 69
67
68
74
00
31
00
6
d 70
5
f 61
75
74
6
f 63
light.
1.
mp_autoc
0160
72
6
f 73
73
68
61
69
72
00
31
00
64
65
63
61
6
c rosshair.
1.
decal
0170
66
72
65
71
75
65
6
e 63
79
00
34
35
00
6
d 70
5
f frequency.
45.
mp_
0180
74
65
61
6
d 6
c 69
73
74
00
68
67
72
75
6
e 74
3
b teamlist.
hgrunt;
0190
73
63
69
65
6
e 74
69
73
74
00
6
d 70
5
f 61
6
c 6
c scientist.
mp_all
01
a0 6
f 77
4
e 50
43
73
00
31
00
6
d 70
5
f 66
72
69
65
owNPCs.
1.
mp_frie
01
b0 6
e 64
6
c 79
66
69
72
65
00
31
00
73
76
5
f 67
72
ndlyfire.
1.
sv_gr
01
c0 61
76
69
74
79
00
38
30
30
00
73
76
5
f 73
74
6
f avity.
800.
sv_sto
01
d0 70
73
70
65
65
64
00
37
35
00
73
76
5
f 6
e 6
f 63
pspeed.
75.
sv_noc
01e0
6
c 69
70
61
63
63
65
6
c 65
72
61
74
65
00
35
00
lipaccelerate.
5.
01
f0 73
76
5
f 6
e 6
f 63
6
c 69
70
73
70
65
65
64
00
35
sv_noclipspeed.
5
0200
00
73
76
5
f 73
70
65
63
61
63
63
65
6
c 65
72
61
.
sv_specaccelera
0210
74
65
00
35
00
73
76
5
f 73
70
65
63
73
70
65
65
te.
5.
sv_specspee
0220
64
00
31
00
73
76
5
f 73
70
65
63
6
e 6
f 63
6
c 69
d.
1.
sv_specnocli
0230
70
00
30
00
73
76
5
f 6
d 61
78
73
70
65
65
64
00
p.
0.
sv_maxspeed.
0240
33
32
30
00
73
76
5
f 61
63
63
65
6
c 65
72
61
74
320.
sv_accelerat
0250
65
00
35
00
73
76
5
f 61
69
72
61
63
63
65
6
c 65
e.
5.
sv_airaccele
0260
72
61
74
65
00
31
30
00
73
76
5
f 77
61
74
65
72
rate.
10.
sv_water
0270
61
63
63
65
6
c 65
72
61
74
65
00
31
30
00
73
76
accelerate.
10.
sv
0280
5
f 77
61
74
65
72
66
72
69
63
74
69
6
f 6
e 00
31
_waterfriction.
1
0290
00
73
76
5
f 66
6
f 6
f 74
73
74
65
70
73
00
31
00
.
sv_footsteps.
1.
02
a0 73
76
5
f 72
6
f 6
c 6
c 73
70
65
65
64
00
32
30
30
sv_rollspeed.
200
02
b0 00
73
76
5
f 72
6
f 6
c 6
c 61
6
e 67
6
c 65
00
30
00
.
sv_rollangle.
0.
02
c0 73
76
5
f 66
72
69
63
74
69
6
f 6
e 00
34
00
73
76
sv_friction.
4.
sv
02
d0 5
f 62
6
f 75
6
e 63
65
00
30
00
73
76
5
f 73
74
65
_bounce.
0.
sv_ste
02e0
70
73
69
7
a 65
00
31
38
00
72
5
f 56
65
68
69
63
psize.
18.
r_Vehic
02
f0 6
c 65
56
69
65
77
44
61
6
d 70
65
6
e 00
31
00
72
leViewDampen.
1.
r
0300
5
f 4
a 65
65
70
56
69
65
77
44
61
6
d 70
65
6
e 46
_JeepViewDampenF
0310
72
65
71
00
37
2
e 30
00
72
5
f 4
a 65
65
70
56
69
req.
7.0
.
r_JeepVi
0320
65
77
44
61
6
d 70
65
6
e 44
61
6
d 70
00
31
2
e 30
ewDampenDamp.
1.0
0330
00
72
5
f 4
a 65
65
70
56
69
65
77
5
a 48
65
69
67
.
r_JeepViewZHeig
0340
68
74
00
31
30
2
e 30
00
72
5
f 41
69
72
62
6
f 61
ht.
10.0
.
r_Airboa
0350
74
56
69
65
77
44
61
6
d 70
65
6
e 46
72
65
71
00
tViewDampenFreq.
0360
37
2
e 30
00
72
5
f 41
69
72
62
6
f 61
74
56
69
65
7.0
.
r_AirboatVie
0370
77
44
61
6
d 70
65
6
e 44
61
6
d 70
00
31
2
e 30
00
wDampenDamp.
1.0
.
0380
72
5
f 41
69
72
62
6
f 61
74
56
69
65
77
5
a 48
65
r_AirboatViewZHe
0390
69
67
68
74
00
30
2
e 30
00
6
d 70
5
f 74
69
6
d 65
ight.
0.0
.
mp_time
03
a0 6
c 69
6
d 69
74
00
35
30
30
00
73
76
5
f 61
6
c 6
c limit.
500.
sv_all
03
b0 74
61
6
c 6
b 00
30
00
6
d 70
5
f 64
79
6
e 61
6
d 69
talk.
0.
mp_dynami
03
c0 63
70
72
69
63
69
6
e 67
00
30
00
6
e 65
78
74
6
c cpricing.
0.
nextl
03
d0 65
76
65
6
c 00
00
6
d 70
5
f 61
75
74
6
f 74
65
61
evel..
mp_autotea
03e0
6
d 62
61
6
c 61
6
e 63
65
00
31
00
6
d 70
5
f 6
d 61
mbalance.
1.
mp_ma
03
f0 78
72
6
f 75
6
e 64
73
00
30
00
6
d 70
5
f 72
6
f 75
xrounds.
0.
mp_rou
0400
6
e 64
74
69
6
d 65
00
34
00
6
d 70
5
f 66
72
65
65
ndtime.
4.
mp_free
0410
7
a 65
74
69
6
d 65
00
35
00
6
d 70
5
f 63
34
74
69
zetime.
5.
mp_c4ti
0420
6
d 65
72
00
33
35
00
6
d 70
5
f 6
c 69
6
d 69
74
74
mer.
35.
mp_limitt
0430
65
61
6
d 73
00
30
00
6
d 70
5
f 68
6
f 73
74
61
67
eams.
0.
mp_hostag
0440
65
70
65
6
e 61
6
c 74
79
00
33
00
73
76
5
f 76
6
f epenalty.
3.
sv_vo
0450
69
63
65
65
6
e 61
62
6
c 65
00
31
00
73
76
5
f 63
iceenable.
1.
sv_c
0460
6
f 6
e 74
61
63
74
00
77
77
77
2
e 63
6
c 61
6
e 6
b ontact.
www.
clank
0470
69
6
c 6
c 65
72
7
a 2
e 64
65
00
73
76
5
f 70
61
75
illerz.
de.
sv_pau
0480
73
61
62
6
c 65
00
30
00
73
76
5
f 63
68
65
61
74
sable.
0.
sv_cheat
0490
73
00
30
00
63
6
f 6
f 70
00
30
00
64
65
61
74
68
s.
0.
coop.
0.
death
04
a0 6
d 61
74
63
68
00
31
00
74
76
5
f 70
61
73
73
77
match.
1.
tv_passw
04
b0 6
f 72
64
00
30
00
74
76
5
f 72
65
6
c 61
79
70
61
ord.
0.
tv_relaypa
04
c0 73
73
77
6
f 72
64
00
30
00
73
76
5
f 70
61
73
73
ssword.
0.
sv_pass
04
d0 77
6
f 72
64
00
30
00
word.
0.
Donnée |
Type |
Explication |
---|---|---|
0xFFFFFF |
int |
- |
Type |
byte |
Type est toujours égal à 'E' (0x45) |
Nombre de règles |
short |
- |
Nom de la règle |
string |
- |
Valeur de la règle |
string |
- |
II-C-3. Script▲
// Constant
define('
PACKET_SIZE
'
,
'
1400
'
);
define('
SERVERQUERY_GETCHALLENGE
'
,
"
\xFF\xFF\xFF\xFF\x57
"
);
define('
SERVERQUERY_RULES
'
,
"
\xFF\xFF\xFF\xFF\x56
"
);
define('
REPLY_GETCHALLENGE
'
,
"
\x41
"
);
define('
REPLY_RULES
'
,
"
\x45
"
);
// Ip address and port
$_ip
=
'
82.149.249.243
'
;
$_port
=
'
27017
'
;
// Open connection with server
$socket
=
stream_socket_client('
udp://
'
.
$_ip
.
'
:
'
.
$_port
,
$errno
,
$errstr
,
30
);
// Get the challenge number
// Send command to server
$cmd
=
SERVERQUERY_GETCHALLENGE;
$length
=
strlen($cmd
);
fwrite($socket
,
$cmd
,
$length
);
// Get response from server
$response
=
fread($socket
,
PACKET_SIZE);
// Filter the response
$pattern
=
"
´
\xFF\xFF\xFF\xFF
"
.
REPLY_GETCHALLENGE.
"
´
"
;
$challengeNumber
=
preg_replace($pattern
,
''
,
$response
);
// Get the server rules info
// Send command to server
$cmd
=
SERVERQUERY_RULES.
$challengeNumber
;
$length
=
strlen($cmd
);
fwrite($socket
,
$cmd
,
$length
);
// Get response from server
$response
=
fread($socket
,
PACKET_SIZE);
// Clean response
$pattern
=
"
#
\xFF\xFF\xFF\xFF
"
.
REPLY_RULES.
"
#
"
;
$response
=
preg_replace($pattern
,
''
,
$response
);
// Number of rules
$ruleNumber
=
getShortSigned($response
);
while ($response
!==
false)
{
//Rule Name - string (The name of the rule)
$name
=
getString($response
);
//Rule Value - string (The rule's value)
$rules
[
$name
]
=
getString($response
);
}
L'affichage du tableau $rules nous donne :
Array
(
[
mattie_mugmod]
=>
1
[
mattie_eventscripts]
=>
1
[
eventscripts_ver]
=>
1.3.0.006
[
mani_reserve_slots]
=>
0
[
mani_admin_plugin_version]
=>
1.2
BetaR VSP
[
mani_tickrate]
=>
100
[
mani_nextmap]
=>
cs_office
[
est_version]
=>
0.417
a
[
mp_teamplay]
=>
0
[
mp_fraglimit]
=>
0
[
mp_falldamage]
=>
1
[
mp_weaponstay]
=>
0
[
mp_forcerespawn]
=>
1
[
mp_footsteps]
=>
1
[
mp_flashlight]
=>
1
[
mp_autocrosshair]
=>
1
[
decalfrequency]
=>
45
[
mp_teamlist]
=>
hgrunt;
scientist
[
mp_allowNPCs]
=>
1
[
mp_friendlyfire]
=>
1
[
sv_gravity]
=>
800
[
sv_stopspeed]
=>
75
[
sv_noclipaccelerate]
=>
5
[
sv_noclipspeed]
=>
5
[
sv_specaccelerate]
=>
5
[
sv_specspeed]
=>
1
[
sv_specnoclip]
=>
0
[
sv_maxspeed]
=>
320
[
sv_accelerate]
=>
5
[
sv_airaccelerate]
=>
10
[
sv_wateraccelerate]
=>
10
[
sv_waterfriction]
=>
1
[
sv_footsteps]
=>
1
[
sv_rollspeed]
=>
200
[
sv_rollangle]
=>
0
[
sv_friction]
=>
4
[
sv_bounce]
=>
0
[
sv_stepsize]
=>
18
[
r_VehicleViewDampen]
=>
1
[
r_JeepViewDampenFreq]
=>
7.0
[
r_JeepViewDampenDamp]
=>
1.0
[
r_JeepViewZHeight]
=>
10.0
[
r_AirboatViewDampenFreq]
=>
7.0
[
r_AirboatViewDampenDamp]
=>
1.0
[
r_AirboatViewZHeight]
=>
0.0
[
mp_timelimit]
=>
500
[
sv_alltalk]
=>
0
[
mp_dynamicpricing]
=>
0
[
nextlevel]
=>
[
mp_autoteambalance]
=>
1
[
mp_maxrounds]
=>
0
[
mp_roundtime]
=>
4
[
mp_freezetime]
=>
5
[
mp_c4timer]
=>
35
[
mp_limitteams]
=>
0
[
mp_hostagepenalty]
=>
3
[
sv_voiceenable]
=>
1
[
sv_contact]
=>
www.
clankillerz.
de
[
sv_pausable]
=>
0
[
sv_cheats]
=>
0
[
coop]
=>
0
[
deathmatch]
=>
1
[
tv_password]
=>
0
[
tv_relaypassword]
=>
0
[
sv_password]
=>
0
)
II-D. Informations sur les joueurs▲
Cette partie consiste à récupérer quelques informations à propos des joueurs présents sur le serveur telles que leur pseudo, nombre de skills et temps présent sur le serveur.
II-D-1. Format de la requête▲
0020
FF FF FF FF 55
<
4
byte challenge number>
....
U
Donnée |
Type |
---|---|
0xFFFFFF |
int |
U (ou 0x55) |
byte |
Challenge Number |
long |
II-D-2. Format de la réponse▲
0020
ff ff ff ff 44
08
....
D.
0030
00
c2 bb 7
d c3 87
4
b c5 bd 7
b c2 ab 30
30
37
7
c ...}..
K..{.
.007
|
0040
6
c 6
f 57
62
49
72
64
00
33
00
00
00
39
97
6
d 45
loWbIrd.
3.
.
.9
.
mE
0050
01
42
69
6
c 6
c 79
74
68
65
4
b 69
64
00
05
00
00
.
BillytheKid....
0060
00
83
01
82
44
02
c5 a0 c4 ac c5 98
c5 8
e e2 84
....
D...........
0070
a2 c2 aa c2 b9 7
c 20
4
c 75
6
b 40
73
31
39
38
39
.....|
Luk@
s1989
0080
28
63
68
29
00
01
00
00
00
3
d 5
a 49
43
03
44
72
(ch).....=
ZIC.
Dr
0090
2
e 45
76
69
6
c 20
5
b 47
45
52
5
d 00
01
00
00
00
.
Evil [
GER].....
00
a0 cf 29
a3 42
04
46
61
74
74
69
58
78
5
e 00
0
e 00
.
).
B.
FattiXx^...
00
b0 00
00
dc 5
f 4
d 44
05
5
e 53
6
f 4
f 6
e 59
20
3
a 3
e ..
._
MD.^
SoOnY :>
00
c0 20
3
a 3
e 20
3
a 3
e 00
01
00
00
00
2
b 23
8
e 42
07
:>
:>.....+
#.B.
00
d0 49
73
4
e 23
50
77
4
e 62
00
01
00
00
00
b4 bd 21
IsN#PwNb.......!
00e0
43
0
d 21
62
45
6
a 6
b 32
31
4
f 2
a 00
1
e 00
00
00
C.!
bEjk21O*.....
00
f0 66
a9 0
b 45
f..
E
Donnée |
Type |
Explication |
---|---|---|
0xFFFFFF |
int |
- |
Type |
byte |
Type est toujours égal à 'D' (0x44) |
Nombre de joueurs |
byte |
- |
Index |
byte |
Index compris entre 0 et le nombre de joueurs |
Nom du joueur |
string |
- |
Kills |
long |
- |
Temps connecté |
float |
Temps en seconde |
II-D-3. Script▲
// Constant
define('
PACKET_SIZE
'
,
'
1400
'
);
define('
SERVERQUERY_GETCHALLENGE
'
,
"
\xFF\xFF\xFF\xFF\x57
"
);
define('
SERVERQUERY_PLAYER
'
,
"
\xFF\xFF\xFF\xFF\x55
"
);
define('
REPLY_GETCHALLENGE
'
,
"
\x41
"
);
define('
REPLY_PLAYER
'
,
"
\x44
"
);
// Ip adress and port
$_ip
=
'
82.149.249.243
'
;
$_port
=
'
27017
'
;
// Open connection with server
$socket
=
stream_socket_client('
udp://
'
.
$_ip
.
'
:
'
.
$_port
,
$errno
,
$errstr
,
30
);
// Get the challenge number
// Send command to server
$cmd
=
SERVERQUERY_GETCHALLENGE;
$length
=
strlen($cmd
);
fwrite($socket
,
$cmd
,
$length
);
// Get response from server
$response
=
fread($socket
,
PACKET_SIZE);
// Filter the response
$pattern
=
"
´
\xFF\xFF\xFF\xFF
"
.
REPLY_GETCHALLENGE.
"
´
"
;
$challengeNumber
=
preg_replace($pattern
,
''
,
$response
);
// Get the server rules info
// Send command to server
$cmd
=
SERVERQUERY_PLAYER.
$challengeNumber
;
$length
=
strlen($cmd
);
fwrite($socket
,
$cmd
,
$length
);
// Get response from server
$response
=
fread($socket
,
PACKET_SIZE);
// Clean response
$pattern
=
"
#
\xFF\xFF\xFF\xFF
"
.
REPLY_PLAYER.
"
#
"
;
$response
=
preg_replace($pattern
,
''
,
$response
);
//Num Players - byte (The number of players reported in this response)
$num
=
getByte($response
);
$i
=
0
;
while ($response
!==
false)
{
//Index - byte (The index into [0.. Num Players] for this entry)
$index
=
getByte($response
);
//Player Name - string (Player's name)
$players
[
$index
][
'
name
'
]
=
trim(getString($response
));
//Kills - long (Number of kills this player has)
$players
[
$index
][
'
kills
'
]
=
getLong($response
);
//Time connected - float (The time in seconds this player has been connected)
$players
[
$index
][
'
time
'
]
=
getFloat($response
);
$i
++;
}
L'affichage du tableau $players nous donne :
Array
(
[
0
]
=>
Array
(
[
name]
=>
»}
Ã&
#8225;KŽ{« 007|loWbIrd
[
kills]
=>
51
[
time]
=>
3801.45141602
)
[
1
]
=>
Array
(
[
name]
=>
BillytheKid
[
kills]
=>
5
[
time]
=>
1040.04724121
)
[
2
]
=>
Array
(
[
name]
=>
Å Ä&
#172;ŘŎ™ª¹| Luk@s1989(ch)
[
kills]
=>
1
[
time]
=>
201.352493286
)
[
3
]
=>
Array
(
[
name]
=>
Dr.
Evil [
GER]
[
kills]
=>
1
[
time]
=>
81.5816574097
)
[
4
]
=>
Array
(
[
name]
=>
FattiXx^
[
kills]
=>
14
[
time]
=>
821.497802734
)
[
5
]
=>
Array
(
[
name]
=>
^
SoOnY :>
:>
:>
[
kills]
=>
1
[
time]
=>
71.068687439
)
[
7
]
=>
Array
(
[
name]
=>
IsN#PwNb
[
kills]
=>
1
[
time]
=>
161.741027832
)
[
13
]
=>
Array
(
[
name]
=>
!
bEjk21O*
[
kills]
=>
30
[
time]
=>
2234.58740234
)
)
III. Commande RCON▲
Les commandes RCON doivent être envoyées via le protocole TCP.
III-A. Format de la requête▲
Donnée |
Type |
Explication |
---|---|---|
Taille du paquet |
int |
- |
Request ID |
int |
Nombre quelconque qui doit être incrémenté entre deux commandes RCON |
Type de la requête |
int |
Deux types de requêtes existent : |
Chaine 1 |
string |
La commande RCON à exécuter (« cvarlist », « changelevel de_dust2 »…) |
Chaine 2 |
string |
Chaine vide |
III-B. Format de la réponse▲
Si la commande RCON : « cvarlist » a été envoyée au serveur, la réponse contient bien plus de caractères que peut contenir un paquet. Il faudra alors gérer les « multipaquets ».
La taille d'un paquet ne dépassera jamais les 4096 bytes.
Steam ne nous permet pas de savoir quand nous sommes en train de lire le dernier paquet. Il faudra donc faire une boucle qui lit les paquets et jouer avec le timeout.
Donnée |
Type |
Explication |
---|---|---|
Taille du paquet |
int |
- |
Request ID |
int |
Le Request ID sera égal à celui envoyé. |
Type de la réponse |
int |
Deux types de réponses existent : |
Chaine 1 |
string |
La réponse à la commande soit un message d'erreur. |
Chaine 2 |
string |
Chaine vide |
III-C. Script▲
La première chose à faire avant d'envoyer la commande au serveur est de s'authentifier grâce à la commande « SERVERDATA_AUTH ».
Si la réponse à cette commande vaut -1, un mauvais mot de passe a été fourni. Si la réponse vaut 2, l'authentification a réussi. Tout autre nombre résulte en un échec de l'authentification.
Si l'authentification est réussie, on peut alors envoyer notre commande.
Si la commande est correcte, vous aurez le résultat sinon un message d'erreur sera fourni.
Il n'y a pas besoin de formater les réponses, car elles sont soit vides, soit en texte normal non formaté.
// Constant
define('
PACKET_SIZE
'
,
'
1400
'
);
define('
SERVERDATA_AUTH
'
,
3
);
define('
SERVERDATA_EXECCOMMAND
'
,
2
);
// Ip adress and port
$_ip
=
'
192.168.1.2
'
;
$_port
=
'
27015
'
;
$_password
=
'
123456789
'
;
$command
=
'
cvarlist
'
;
$s2
=
''
;
$requestId
=
1
;
// -- Open connection with server
$socket
=
stream_socket_client('
tcp://
'
.
$_ip
.
'
:
'
.
$_port
,
$errno
,
$errstr
,
30
);
stream_set_timeout($socket
,
1
,
0
);
// -- Send auth packet
// Construct packet
$data
=
pack("
VV
"
,
$requestId
,
SERVERDATA_AUTH).
$_password
.
chr(0
).
$s2
.
chr(0
);
// Prefix the packet by it's size
$data
=
pack("
V
"
,
strlen($data
)).
$data
;
// Send packet
fwrite($socket
,
$data
,
strlen($data
));
$requestId
++;
// Check if auth is successful
$junk
=
fread($socket
,
PACKET_SIZE);
$string
=
fread($socket
,
PACKET_SIZE);
$size
=
getLong($string
);
$id
=
getLong($string
);
if ($id
==
-
1
)
{
// Error
die('
Auth failed : bad password !
'
);
}
// -- Send command
// Construct packet
$data
=
pack("
VV
"
,
$requestId
,
SERVERDATA_EXECCOMMAND).
$command
.
chr(0
).
$s2
.
chr(0
);
// Prefix the packet by it's size
$data
=
pack("
V
"
,
strlen($data
)).
$data
;
// Send packet
fwrite($socket
,
$data
,
strlen($data
));
$requestId
++;
// -- Read response
$i
=
0
;
$text
=
''
;
while ($string
=
fread($socket
,
4
))
{
$info
[
$i
][
'
size
'
]
=
getLong($string
);
$string
=
fread($socket
,
$info
[
$i
][
'
size
'
]
);
$info
[
$i
][
'
id
'
]
=
getLong($string
);
$info
[
$i
][
'
type
'
]
=
getLong($string
);
$info
[
$i
][
'
s1
'
]
=
getString($string
);
$info
[
$i
][
'
s2
'
]
=
getString($string
);
$text
.=
$info
[
$i
][
'
s1
'
];
}
IV. Lister les serveurs▲
Pour récupérer la liste des serveurs, il faut envoyer une requête spéciale au « master server » de Steam. Notez que cette liste n'est pas à 100 % fiable.
Le serveur à l'adresse : hl2master.steampowered.com:27011. Son IP pourra changer, donc il faudra appliquer la fonction gethostbyname()gethostbyname() avant de passer l'IP à la fonction de connexion aux serveurs.
Cette requête doit se faire via le protocole UDP.
IV-A. Format de la requête▲
0020
31
03
30
2
e 30
2
e 1.0.0
.
0030
30
2
e 30
3
a 30
00
5
c 74
79
70
65
5
c 64
5
c 73
65
0.0
:
0.
\type\d\se
0040
63
75
72
65
5
c 31
5
c 67
61
6
d 65
64
69
72
5
c 63
cure\1
\gamedir\c
0050
73
74
72
69
6
b 65
5
c 6
d 61
70
5
c 64
65
5
f 64
75
strike\map\de_du
0060
73
74
32
00
st2.
Donnée |
Type |
Explication |
---|---|---|
1 (ou 0x31) |
byte |
- |
Code de la région |
byte |
Voir le tableau ci-dessous pour la liste complète des régions. |
Adresse IP : port |
string |
L'adresse IP de départ est 0.0.0.0:0. |
0x00 |
byte |
- |
Filtre |
string |
Un filtre permet de spécifier des critères de recherche tels que le nom du jeu, la carte, l'os… |
0x00 |
byte |
- |
Voici la liste des différentes régions :
Byte |
Description |
---|---|
0x00 |
Côte Est des États-Unis |
0x01 |
Côte Ouest des États-Unis |
0x02 |
Amérique du Sud |
0x03 |
Europe |
0x04 |
Asie |
0x05 |
Australie |
0x06 |
Moyen-Orient |
0x07 |
Afrique |
0xFF |
Toute - Autre |
Voici la liste des différents filtres :
String |
Description |
---|---|
\type\d |
Serveur dédié |
\secure\1 |
Serveur sécurisé |
\gamedir\[mod] |
Jeu sur le serveur (ex. : cstrike) |
\map\[map] |
Carte sur le serveur |
\linux\1 |
Serveur linux |
\empty\1 |
Serveur non vide |
\full\1 |
Serveur non rempli |
\proxy\1 |
Serveur qui a des spectateurs |
IV-B. Format de la réponse▲
0020
ff ff ff ff 66
0
a ....
f.
0030
50
c6 7
c 56
69
90
50
be fd dc 69
aa 50
dd 1
e ac P.|
Vi.
P...
i.
P...
0040
69
88
50
be fb c2 69
c8 50
be fb c2 69
8
d 50
be i.
P...
i.
P...
i.
P.
0050
f8 33
69
88
50
ed cb df 69
87
50
be f6 2
f 69
87
.3
i.
P...
i.
P../
i.
...
0570
51
13
db 21
69
87
50
be 48
1
f 69
87
51
13
db 21
Q..!
i.
P.
H.
i.
Q..!
0580
69
91
51
13
db 24
69
87
51
13
db 26
69
87
50
be i.
Q..
$i
.
Q..&
i.
P.
0590
48
0
e 69
87
51
13
db 27
69
87
H.
i.
Q..
'
i.
Donnée |
Type |
Explication |
---|---|---|
0xFFFFFF |
int |
- |
Type |
byte |
Type est toujours égal à '0x660A' |
1re partie de l'adresse IP |
byte |
- |
2e partie de l'adresse IP |
byte |
- |
3e partie de l'adresse IP |
byte |
- |
4e partie de l'adresse IP |
byte |
- |
Port |
unsigned short |
- |
IV-C. Script▲
Pour récupérer la liste des serveurs il faut faire une boucle qui s'exécutera tant que la dernière IP trouvée n'est pas 0.0.0.0:0.
// Constant
define('
PACKET_SIZE
'
,
1400
);
// Master Server info
define('
MASTER_SERVER_HOST
'
,
'
hl2master.steampowered.com
'
);
define('
MASTER_SERVER_PORT
'
,
27011
);
define('
MASTER_SERVER_QUERY
'
,
"
\x31
"
);
define('
MASTER_SERVER_REPLY
'
,
"
\x66\x0A
"
);
define('
MASTER_SERVER_IP
'
,
"
0.0.0.0:0
"
);
// Areas
define('
REGION_US_EAST
'
,
"
\x00
"
);
define('
REGION_US_WEST
'
,
"
\x01
"
);
define('
REGION_US_SOUTH
'
,
"
\x02
"
);
define('
REGION_EUROPE
'
,
"
\x03
"
);
define('
REGION_ASIA
'
,
"
\x04
"
);
define('
REGION_AUSTRALIA
'
,
"
\x05
"
);
define('
REGION_MIDDLE_EAST
'
,
"
\x06
"
);
define('
REGION_AFRICA
'
,
"
\x07
"
);
define('
REGION_OTHER
'
,
"
\xFF
"
);
// Filters
define('
FILTER_ALL
'
,
''
);
define('
FILTER_DEDICATED
'
,
"
\x5C\x74\x79\x70\x65\x5C\x64
"
);
//"\type\d
define('
FILTER_SECURE
'
,
"
\x5C\x73\x65\x63\x75\x72\x65\x5C\x31
"
);
//"\secure\1
define('
FILTER_GAME
'
,
"
\x5C\x67\x61\x6D\x65\x64\x69\x72\x5C\x63\x73\x74\x72\x69\x6B\x65
"
);
//"\gamedir\cstrike
define('
FILTER_MAP_DE_DUST2
'
,
"
\x5C\x6D\x61\x70\x5C\x64\x65\x5F\x64\x75\x73\x74\x32
"
);
//"\map\de_dust2
define('
FILTER_LINUX
'
,
"
\x5C\x6C\x69\x6E\x75\x78\x5C\x31
"
);
//"\linux\1
define('
FILTER_NOT_EMPTY
'
,
"
\x5C\x65\x6D\x70\x74\x79\x5C\x31
"
);
//"\empty\1
define('
FILTER_NOT_FULL
'
,
"
\x5C\x66\x75\x6C\x6C\x5C\x31
"
);
//"\full\1
define('
FILTER_PROXY
'
,
"
\x5C\x70\x72\x6F\x78\x79\x5C\x31
"
);
//"\proxy\1
// Open connection with server
$socket
=
stream_socket_client(
'
udp://
'
.
gethostbyname(MASTER_SERVER_HOST).
'
:
'
.
MASTER_SERVER_PORT,
$errno
,
$errstr
,
30
);
$list
=
array();
// Start IP
$currentIP
=
MASTER_SERVER_IP;
do
{
// Construct request packet, min must contain :
// Byte - Message Type (0x31 - the character "1")
// Byte - Region Code
// String Zero - IP:Port
// String Zero - Filter
$packet
=
MASTER_SERVER_QUERY
.
REGION_OTHER
.
$currentIP
.
"
\x00
"
.
"
\x00
"
;
// Send packet
fwrite($socket
,
$packet
);
// Retreive data
$string
=
fread($socket
,
PACKET_SIZE);
// Clean response
$pattern
=
"
#
\xFF\xFF\xFF\xFF
"
.
MASTER_SERVER_REPLY.
"
#
"
;
$response
=
preg_replace($pattern
,
''
,
$string
);
while ($string
)
{
// Parse data
// IP adress - 4 Byte
$ip
=
getByte($string
);
$ip
.=
'
.
'
.
getByte($string
);
$ip
.=
'
.
'
.
getByte($string
);
$ip
.=
'
.
'
.
getByte($string
);
// Port number - unsigned short
$port
=
getShortUnsigned($string
);
$list
[]
=
array(
'
ip
'
=>
$ip
,
'
port
'
=>
$port
);
$currentIP
=
$ip
.
'
:
'
.
$port
;
}
}
while($currentIP
!=
MASTER_SERVER_IP);
var_dump($list
);
Ce bout de code a pour résultat une erreur bien sûr. Le nombre de serveurs récupérés de cette façon (sans aucun filtre) dépasse de loin la taille de mémoire autorisée par défaut par PHP.
Voici la variable $packet avec des filtres.
$packet
=
MASTER_SERVER_QUERY
.
REGION_EUROPE
.
$currentIP
.
"
\x00
"
.
FILTER_DEDICATED
.
FILTER_SECURE
.
FILTER_GAME
.
FILTER_MAP_DE_DUST2
.
"
\x00
"
;
V. Scripts divers▲
V-A. Voir l'état du serveur▲
Ce script permet de voir si le serveur est disponible ou s'il est déconnecté.
Pour ce faire, on peut tout simplement utiliser le premier script de cet article (celui qui récupère le « challenge number ») et tester si la réponse obtenue est vide ou pas.
Ceci étant la manière de faire la plus sure, car elle vérifie en même temps si le serveur accepte le protocole de Steam.
Il ne faut par contre pas oublier de spécifier un timeout, car si le server est hors-ligne, il va bloquer la page pendant le nombre secondes définit par le timeout par défaut (30).
// Ip address and port
$_ip
=
'
82.149.249.243
'
;
$_port
=
'
2707
'
;
// Open a connection with server
$socket
=
stream_socket_client('
udp://
'
.
$_ip
.
'
:
'
.
$_port
,
$errno
,
$errstr
,
30
);
stream_set_timeout($socket
,
1
,
0
);
// Send command to server
$cmd
=
SERVERQUERY_GETCHALLENGE;
$length
=
strlen($cmd
);
fwrite($socket
,
$cmd
,
$length
);
// Get response from server
$response
=
fread($socket
,
PACKET_SIZE);
if (empty($response
))
{
echo '
Server Offline
'
;
}
else
{
echo '
Server Online
'
;
}
V-B. Fonctions utilisées▲
/**
* Return a byte and split it out of the string
* - unsigned char
*
*
@param
string
$string
String
*/
function getByte(&
$string
)
{
$data
=
substr($string
,
0
,
1
);
$string
=
substr($string
,
1
);
$data
=
unpack('
Cvalue
'
,
$data
);
return $data
[
'
value
'
];
}
/**
* Return an unsigned short and split it out of the string
* - unsigned short (
16
bit, big endian byte order)
*
*
@param
string
$string
String
*/
function getShortUnsigned(&
$string
)
{
$data
=
substr($string
,
0
,
2
);
$string
=
substr($string
,
2
);
$data
=
unpack('
nvalue
'
,
$data
);
return $data
[
'
value
'
];
}
/**
* Return a signed short and split it out of the string
* - signed short (
16
bit, machine byte order)
*
*
@param
string
$string
String
*/
function getShortSigned(&
$string
)
{
$data
=
substr($string
,
0
,
2
);
$string
=
substr($string
,
2
);
$data
=
unpack('
svalue
'
,
$data
);
return $data
[
'
value
'
];
}
/**
* Return a long and split it out of the string
* - unsigned long (
32
bit, little endian byte order)
*
*
@param
string
$string
String
*/
function getLong(&
$string
)
{
$data
=
substr($string
,
0
,
4
);
$string
=
substr($string
,
4
);
$data
=
unpack('
Vvalue
'
,
$data
);
return $data
[
'
value
'
];
}
/**
* Return a float and split it out of the string
*
*
@param
string
$string
String
*/
function getFloat(&
$string
)
{
$data
=
substr($string
,
0
,
4
);
$string
=
substr($string
,
4
);
$array
=
unpack("
fvalue
"
,
$data
);
return $array
[
'
value
'
];
}
/**
* Return a string and split it out of the string
*
*
@param
string
$string
String
*/
function getString(&
$string
)
{
$data
=
""
;
$byte
=
substr($string
,
0
,
1
);
$string
=
substr($string
,
1
);
while (ord($byte
) !=
"
0
"
)
{
$data
.=
$byte
;
$byte
=
substr($string
,
0
,
1
);
$string
=
substr($string
,
1
);
}
return $data
;
}
V-C. Classe▲
Voici une classe qui regroupe toutes les fonctionnalités vues précédemment.
Classe à télécharger ici.Classe pour dialoguer avec un serveur Counter-Strike Source
VI. Liens et remerciements▲
Tous mes remerciements à YoguiYogui et lokaloka pour leur relecture.