Difference between revisions of "How to query a CS Server"
(added wikilinks and some missing indenting) |
m (→See also: fixed grammer) |
||
(16 intermediate revisions by 6 users not shown) | |||
Line 1: | Line 1: | ||
− | If you want to e.g. get the | + | If you want to e.g. get the number and names of all player who are currently playing on a CS-server, you have to send a so-called "query" to this server (using UDP) and handle everything the server sends back to you. This isn't as easy as it sounds. At first, you would have to 'learn', or, at least, understand the protocol a CS-server uses (e.g. what do I have to send to the server to get a 'good' reply and what's the structure of this reply). If you want to learn more about these protocols, take a look at http://developer.valvesoftware.com/wiki/Server_Queries . |
− | The following script '''echo'''s the output to your active window. You will have to modify it to be able to use it as 'bot'. It's a more complex script and you don't need to understand it ( | + | The following script '''echo'''s the output to your active window. You will have to modify it to be able to use it as 'bot'. It's a more complex script and you don't need to understand it (completely) to be able to use it. |
; HL server query snippet by Saturn | ; HL server query snippet by Saturn | ||
+ | ; | ||
+ | ; 16-04-2009: update to protocol version 48 by NaNg | ||
+ | ; 29-01-2011: update to fix UTF8 problem in mIRC 7.* and fixed name problems if last char is space by NaNg | ||
; | ; | ||
; usage: | ; usage: | ||
; /hlinfo <ip> <port> | ; /hlinfo <ip> <port> | ||
+ | ; or: /hlinfo <ip>:<port> | ||
; /hlplay <ip> <port> | ; /hlplay <ip> <port> | ||
− | + | ; or: /hlplay <ip>:<port> | |
+ | |||
; the alias that opens a new socket if you want to get general information about a running CS-server | ; the alias that opens a new socket if you want to get general information about a running CS-server | ||
[[alias]] hlinfo { | [[alias]] hlinfo { | ||
+ | [[If-Then-Else|if]] (!$2) [[tokenize]] 58 [[$1-|$1]] | ||
+ | |||
; using a dynamic socket name so we can use multiple instances at once | ; using a dynamic socket name so we can use multiple instances at once | ||
− | [[var]] %sock = $+(hlinfo-,[[$ticks]]) | + | [[var]] %sock = [[DollarPlus|$+]](hlinfo-,[[$ticks]]) |
− | ; actually open the socket | + | ; construct the query to send to the server |
− | [[sockudp]] -k %sock | + | [[bset]] &t 1 255 255 255 255 |
+ | bset -t &t 5 TSource Engine Query | ||
+ | bset &t 25 0 | ||
+ | ; actually open the socket, and send the query | ||
+ | [[sockudp]] -k %sock $1-2 &t | ||
; closing the socket after 60seconds if there is no response (else it will be closed by another event (see below)) | ; closing the socket after 60seconds if there is no response (else it will be closed by another event (see below)) | ||
.[[timer]] $+ %sock 1 60 [[sockclose]] %sock | .[[timer]] $+ %sock 1 60 [[sockclose]] %sock | ||
} | } | ||
− | + | ||
− | ; this event will get all data | + | ; this event will get all data sent back from the server |
[[on_udpread|on *:UDPREAD]]:hlinfo-*:{ | [[on_udpread|on *:UDPREAD]]:hlinfo-*:{ | ||
; save it in a binary variable | ; save it in a binary variable | ||
[[sockread]] -f &reply | [[sockread]] -f &reply | ||
− | + | ||
; set some local variables that we will use below | ; set some local variables that we will use below | ||
var %offset, %name, %map, %game, %num, %max, %ip, %dir | var %offset, %name, %map, %game, %num, %max, %ip, %dir | ||
− | ; following the protocol, this "m" shows us that we | + | ; following the protocol, this "m" shows us that we queried a Half-Life 1 server |
− | if ($chr([[$bvar]](&reply,5)) == m) { | + | if ([[$chr]]([[$bvar]](&reply,5)) == m) { |
; Half-Life 1 info reply | ; Half-Life 1 info reply | ||
− | + | ||
%offset = 6 | %offset = 6 | ||
− | ; | + | ; save the ip |
%ip = $bvar(&reply,%offset,128).text | %ip = $bvar(&reply,%offset,128).text | ||
− | [[inc]] %offset [[$calc]]([[$len]](% | + | [[inc]] %offset [[$calc]]([[$len]]($bvar(&reply,%offset,128).text) + 1) |
− | + | ||
; the same | ; the same | ||
%name = $bvar(&reply,%offset,128).text | %name = $bvar(&reply,%offset,128).text | ||
− | inc %offset $calc($len(% | + | inc %offset $calc($len($bvar(&reply,%offset,128).text) + 1) |
− | + | ||
; the current map | ; the current map | ||
%map = $bvar(&reply,%offset,128).text | %map = $bvar(&reply,%offset,128).text | ||
− | inc %offset $calc($len(% | + | inc %offset $calc($len($bvar(&reply,%offset,128).text) + 1) |
− | + | ||
; the game directory | ; the game directory | ||
%dir = $bvar(&reply,%offset,128).text | %dir = $bvar(&reply,%offset,128).text | ||
− | inc %offset $calc($len(% | + | inc %offset $calc($len($bvar(&reply,%offset,128).text) + 1) |
− | + | ||
; the name of the game | ; the name of the game | ||
%game = $bvar(&reply,%offset,128).text | %game = $bvar(&reply,%offset,128).text | ||
− | inc %offset $calc($len(% | + | inc %offset $calc($len($bvar(&reply,%offset,128).text) + 1) |
− | + | ||
; current and maximum players | ; current and maximum players | ||
%num = $bvar(&reply,%offset) | %num = $bvar(&reply,%offset) | ||
%max = $bvar(&reply,$calc(%offset + 1)) | %max = $bvar(&reply,$calc(%offset + 1)) | ||
} | } | ||
+ | |||
; else we get data for a CS:Source game | ; else we get data for a CS:Source game | ||
else { | else { | ||
Line 63: | Line 75: | ||
; we do the same as above | ; we do the same as above | ||
%offset = 7 | %offset = 7 | ||
− | + | ||
%name = $bvar(&reply,%offset,128).text | %name = $bvar(&reply,%offset,128).text | ||
− | inc %offset $calc($len(% | + | inc %offset $calc($len($bvar(&reply,%offset,128).text) + 1) |
− | + | ||
%map = $bvar(&reply,%offset,128).text | %map = $bvar(&reply,%offset,128).text | ||
− | inc %offset $calc($len(% | + | inc %offset $calc($len($bvar(&reply,%offset,128).text) + 1) |
− | + | ||
%dir = $bvar(&reply,%offset,128).text | %dir = $bvar(&reply,%offset,128).text | ||
− | inc %offset $calc($len(% | + | inc %offset $calc($len($bvar(&reply,%offset,128).text) + 1) |
− | + | ||
%game = $bvar(&reply,%offset,128).text | %game = $bvar(&reply,%offset,128).text | ||
− | inc %offset $calc($len(% | + | inc %offset $calc($len($bvar(&reply,%offset,128).text) + 1) |
− | + | ||
%num = $bvar(&reply,$calc(%offset + 2)) | %num = $bvar(&reply,$calc(%offset + 2)) | ||
%max = $bvar(&reply,$calc(%offset + 3)) | %max = $bvar(&reply,$calc(%offset + 3)) | ||
Line 86: | Line 98: | ||
echo -a Game: %game | echo -a Game: %game | ||
echo -a Players: %num $+ / $+ %max | echo -a Players: %num $+ / $+ %max | ||
− | + | ||
− | ; and turn the timer | + | ; and turn off the timer closing the socket |
.timer $+ $sockname off | .timer $+ $sockname off | ||
− | ; as we can close it manually | + | ; as we can close it now manually |
− | sockclose $sockname | + | [[sockclose]] $sockname |
} | } | ||
− | + | ||
− | + | ||
− | ; this is the alias that will be called from hlplay to open the socket | + | ; this is the alias that will be called from hlplay to open the socket |
alias hlchal { | alias hlchal { | ||
; $1 = ip, $2 = port, $3- = what to call when received | ; $1 = ip, $2 = port, $3- = what to call when received | ||
; use dynamic socket name as above | ; use dynamic socket name as above | ||
var %sock = $+(hlchal-,$ticks) | var %sock = $+(hlchal-,$ticks) | ||
− | ; open the socket | + | ; construct the query to send to the server |
− | sockudp -k %sock $1-2 | + | bset &t 1 255 255 255 255 85 255 255 255 255 |
− | ; mark it with the ip and port (and the query we want to call when we've received something from the server | + | ; open the socket, and ask for a player challenge number |
+ | sockudp -k %sock $1-2 &t | ||
+ | ; mark it with the ip and port (and the query we want to call when we've received something from the server) | ||
[[sockmark]] %sock $3- $1-2 | [[sockmark]] %sock $3- $1-2 | ||
; and make a new timer to close it after 60 seconds | ; and make a new timer to close it after 60 seconds | ||
.timer $+ %sock 1 60 sockclose %sock | .timer $+ %sock 1 60 sockclose %sock | ||
} | } | ||
− | + | ||
; read everything that comes back from the hlchal-* query | ; read everything that comes back from the hlchal-* query | ||
on *:UDPREAD:hlchal-*:{ | on *:UDPREAD:hlchal-*:{ | ||
Line 117: | Line 131: | ||
sockclose $sockname | sockclose $sockname | ||
} | } | ||
− | + | ||
− | ; the alias we can call to make everything easier | + | ; the alias we can call to make everything easier |
; it will call other aliases to open sockets etc. | ; it will call other aliases to open sockets etc. | ||
alias hlplay { | alias hlplay { | ||
+ | if (!$2) tokenize 58 $1 | ||
hlchal $1-2 hlplay_query | hlchal $1-2 hlplay_query | ||
} | } | ||
− | + | ||
− | ; the alias that opens the _real_ socket to receive data (everything before was just to challenge the server (read the website mentioned above) | + | ; the alias that opens the _real_ socket to receive data (everything before was just to challenge the server (read the website mentioned above)) |
alias hlplay_query { | alias hlplay_query { | ||
; set a binary variable | ; set a binary variable | ||
Line 132: | Line 147: | ||
; open the socket | ; open the socket | ||
sockudp -k %sock $1-2 &query | sockudp -k %sock $1-2 &query | ||
− | ; and turn the | + | ; and turn on the timer closing the socket after 60seconds |
.timer $+ %sock 1 60 sockclose %sock | .timer $+ %sock 1 60 sockclose %sock | ||
} | } | ||
− | + | ||
− | ; this event will receive all data from the server as response to | + | ; this event will receive all data from the server as response to our query |
on *:UDPREAD:hlplay-*:{ | on *:UDPREAD:hlplay-*:{ | ||
; we save it in &reply | ; we save it in &reply | ||
Line 144: | Line 159: | ||
var %i = 1, %num = $bvar(&reply,6), %offset = 7 | var %i = 1, %num = $bvar(&reply,6), %offset = 7 | ||
var %count = 0, %name, %kills | var %count = 0, %name, %kills | ||
− | + | ||
; and echo the number of players | ; and echo the number of players | ||
echo -a Players on $sock($sockname).saddr $+ : $+ $sock($sockname).sport | echo -a Players on $sock($sockname).saddr $+ : $+ $sock($sockname).sport | ||
− | + | ||
; now we can loop through all player and save some details about them | ; now we can loop through all player and save some details about them | ||
− | [[while]] (%i | + | [[while]] (%i <= %num) { |
inc %offset | inc %offset | ||
− | + | ||
; the name | ; the name | ||
%name = $bvar(&reply,%offset,128).text | %name = $bvar(&reply,%offset,128).text | ||
− | inc %offset $calc($len(% | + | inc %offset $calc($len($bvar(&reply,%offset,128).text) + 1) |
− | + | ||
; the kills | ; the kills | ||
%kills = $bvar(&reply,%offset).long | %kills = $bvar(&reply,%offset).long | ||
− | + | if ($isbit(%kills,32)) dec %kills $calc(2^32) | |
inc %offset 8 | inc %offset 8 | ||
− | + | ||
; and if there was a player | ; and if there was a player | ||
− | if (%name | + | if (%name != $null) { |
; increase the variable that shows us the number of the player | ; increase the variable that shows us the number of the player | ||
inc %count | inc %count | ||
Line 173: | Line 188: | ||
} | } | ||
− | ; if there are no players online, echo | + | ; if there are no players online, echo it aswell |
− | if (%count | + | if (%count == 0) echo -a No players found! |
− | + | ||
; turn off the timer | ; turn off the timer | ||
.timer $+ $sockname off | .timer $+ $sockname off | ||
Line 181: | Line 196: | ||
sockclose $sockname | sockclose $sockname | ||
} | } | ||
+ | |||
+ | ==See also== | ||
+ | * [[How to parse HL log data]] | ||
+ | * [[How to RCON an HL1 engine based server]] | ||
+ | * [[How to RCON a source engine based server]] | ||
+ | * [http://developer.valvesoftware.com/wiki/Server_Queries Server Queries on valvesoftware] | ||
+ | |||
[[Category:Tutorials]] | [[Category:Tutorials]] | ||
[[Category:Socket]] | [[Category:Socket]] |
Latest revision as of 20:19, 11 June 2011
If you want to e.g. get the number and names of all player who are currently playing on a CS-server, you have to send a so-called "query" to this server (using UDP) and handle everything the server sends back to you. This isn't as easy as it sounds. At first, you would have to 'learn', or, at least, understand the protocol a CS-server uses (e.g. what do I have to send to the server to get a 'good' reply and what's the structure of this reply). If you want to learn more about these protocols, take a look at http://developer.valvesoftware.com/wiki/Server_Queries .
The following script echos the output to your active window. You will have to modify it to be able to use it as 'bot'. It's a more complex script and you don't need to understand it (completely) to be able to use it.
; HL server query snippet by Saturn ; ; 16-04-2009: update to protocol version 48 by NaNg ; 29-01-2011: update to fix UTF8 problem in mIRC 7.* and fixed name problems if last char is space by NaNg ; ; usage: ; /hlinfo <ip> <port> ; or: /hlinfo <ip>:<port> ; /hlplay <ip> <port> ; or: /hlplay <ip>:<port> ; the alias that opens a new socket if you want to get general information about a running CS-server alias hlinfo { if (!$2) tokenize 58 $1 ; using a dynamic socket name so we can use multiple instances at once var %sock = $+(hlinfo-,$ticks) ; construct the query to send to the server bset &t 1 255 255 255 255 bset -t &t 5 TSource Engine Query bset &t 25 0 ; actually open the socket, and send the query sockudp -k %sock $1-2 &t ; closing the socket after 60seconds if there is no response (else it will be closed by another event (see below)) .timer $+ %sock 1 60 sockclose %sock } ; this event will get all data sent back from the server on *:UDPREAD:hlinfo-*:{ ; save it in a binary variable sockread -f &reply ; set some local variables that we will use below var %offset, %name, %map, %game, %num, %max, %ip, %dir ; following the protocol, this "m" shows us that we queried a Half-Life 1 server if ($chr($bvar(&reply,5)) == m) { ; Half-Life 1 info reply %offset = 6 ; save the ip %ip = $bvar(&reply,%offset,128).text inc %offset $calc($len($bvar(&reply,%offset,128).text) + 1) ; the same %name = $bvar(&reply,%offset,128).text inc %offset $calc($len($bvar(&reply,%offset,128).text) + 1) ; the current map %map = $bvar(&reply,%offset,128).text inc %offset $calc($len($bvar(&reply,%offset,128).text) + 1) ; the game directory %dir = $bvar(&reply,%offset,128).text inc %offset $calc($len($bvar(&reply,%offset,128).text) + 1) ; the name of the game %game = $bvar(&reply,%offset,128).text inc %offset $calc($len($bvar(&reply,%offset,128).text) + 1) ; current and maximum players %num = $bvar(&reply,%offset) %max = $bvar(&reply,$calc(%offset + 1)) } ; else we get data for a CS:Source game else { ; Source info reply ; we do the same as above %offset = 7 %name = $bvar(&reply,%offset,128).text inc %offset $calc($len($bvar(&reply,%offset,128).text) + 1) %map = $bvar(&reply,%offset,128).text inc %offset $calc($len($bvar(&reply,%offset,128).text) + 1) %dir = $bvar(&reply,%offset,128).text inc %offset $calc($len($bvar(&reply,%offset,128).text) + 1) %game = $bvar(&reply,%offset,128).text inc %offset $calc($len($bvar(&reply,%offset,128).text) + 1) %num = $bvar(&reply,$calc(%offset + 2)) %max = $bvar(&reply,$calc(%offset + 3)) } ; now we echo all stored details to the active window echo -a Info for $sock($sockname).saddr $+ : $+ $sock($sockname).sport echo -a Name: %name echo -a Map: %map echo -a Game: %game echo -a Players: %num $+ / $+ %max ; and turn off the timer closing the socket .timer $+ $sockname off ; as we can close it now manually sockclose $sockname } ; this is the alias that will be called from hlplay to open the socket alias hlchal { ; $1 = ip, $2 = port, $3- = what to call when received ; use dynamic socket name as above var %sock = $+(hlchal-,$ticks) ; construct the query to send to the server bset &t 1 255 255 255 255 85 255 255 255 255 ; open the socket, and ask for a player challenge number sockudp -k %sock $1-2 &t ; mark it with the ip and port (and the query we want to call when we've received something from the server) sockmark %sock $3- $1-2 ; and make a new timer to close it after 60 seconds .timer $+ %sock 1 60 sockclose %sock } ; read everything that comes back from the hlchal-* query on *:UDPREAD:hlchal-*:{ ; and save it in the binary variable called &reply sockread -f &reply ; actually call the alias saved in the sockmark (hlplay_query) $sock($sockname).mark $bvar(&reply,6,4) ; and turn the timer off because we can close it manually .timer $+ $sockname off sockclose $sockname } ; the alias we can call to make everything easier ; it will call other aliases to open sockets etc. alias hlplay { if (!$2) tokenize 58 $1 hlchal $1-2 hlplay_query } ; the alias that opens the _real_ socket to receive data (everything before was just to challenge the server (read the website mentioned above)) alias hlplay_query { ; set a binary variable bset &query 1 255 255 255 255 85 $3- ; use dynamic socket names again var %sock = $+(hlplay-,$ticks) ; open the socket sockudp -k %sock $1-2 &query ; and turn on the timer closing the socket after 60seconds .timer $+ %sock 1 60 sockclose %sock } ; this event will receive all data from the server as response to our query on *:UDPREAD:hlplay-*:{ ; we save it in &reply sockread -f &reply ; set some local variables we will use later var %i = 1, %num = $bvar(&reply,6), %offset = 7 var %count = 0, %name, %kills ; and echo the number of players echo -a Players on $sock($sockname).saddr $+ : $+ $sock($sockname).sport ; now we can loop through all player and save some details about them while (%i <= %num) { inc %offset ; the name %name = $bvar(&reply,%offset,128).text inc %offset $calc($len($bvar(&reply,%offset,128).text) + 1) ; the kills %kills = $bvar(&reply,%offset).long if ($isbit(%kills,32)) dec %kills $calc(2^32) inc %offset 8 ; and if there was a player if (%name != $null) { ; increase the variable that shows us the number of the player inc %count ; and echo it to the active window with the kills saved above echo -a %count $+ . %name - %kills } ; increase %i so we go on with the next player inc %i } ; if there are no players online, echo it aswell if (%count == 0) echo -a No players found! ; turn off the timer .timer $+ $sockname off ; and close the socket manually sockclose $sockname }