Difference between revisions of "How to query a CS Server"

From Scriptwiki
Jump to: navigation, search
m (update by nang)
m (See also: fixed grammer)
 
(12 intermediate revisions by 3 users not shown)
Line 1: Line 1:
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://dev.kquery.com/ or http://www.valve-erc.com/srcsdk/Code/Networking/serverqueries.html or http://developer.valvesoftware.com/wiki/Server_Queries .
+
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 (completely) to be able to use 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
+
; 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
+
; 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>
+
; or: /hlinfo <ip>:<port>
  ; /hlplay <ip> <port>
+
; /hlplay <ip> <port>
  ; or: /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 (!$2) tokenize 58 $1
+
  [[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]])
    ; construct the query to send to the server
+
  ; construct the query to send to the server
    bset &t 1 255 255 255 255
+
  [[bset]] &t 1 255 255 255 255
    bset -t &t 5 TSource Engine Query
+
  bset -t &t 5 TSource Engine Query
    bset &t 25 0
+
  bset &t 25 0
    ; actually open the socket, and send the query
+
  ; actually open the socket, and send the query
    sockudp -k %sock $1-2 &t
+
  [[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 sent back from the server
+
; this event will get all data sent back from the server
  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 queried a Half-Life 1 server
+
   ; 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
+
    ; save the ip
      %ip = $bvar(&reply,%offset,128).text
+
    %ip = $bvar(&reply,%offset,128).text
      inc %offset $calc($len($bvar(&reply,%offset,128).text) + 1)
+
    [[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($bvar(&reply,%offset,128).text) + 1)
+
    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($bvar(&reply,%offset,128).text) + 1)
+
    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($bvar(&reply,%offset,128).text) + 1)
+
    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($bvar(&reply,%offset,128).text) + 1)
+
    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 {
      ; Source info reply
+
    ; Source info reply
      ; 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($bvar(&reply,%offset,128).text) + 1)
+
    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($bvar(&reply,%offset,128).text) + 1)
+
    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($bvar(&reply,%offset,128).text) + 1)
+
    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($bvar(&reply,%offset,128).text) + 1)
+
    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))
    }
+
  }
    
+
 
    ; now we echo all stored details to the active window
+
   ; now we echo all stored details to the active window
    echo -a Info for $sock($sockname).saddr $+ : $+ $sock($sockname).sport
+
  [[echo]] -a Info for [[$sock]]([[$sockname]]).saddr $+ : $+ $sock($sockname).sport
    echo -a Name: %name
+
  echo -a Name: %name
    echo -a Map: %map
+
  echo -a Map: %map
    echo -a Game: %game
+
  echo -a Game: %game
    echo -a Players: %num $+ / $+ %max
+
  echo -a Players: %num $+ / $+ %max
    
+
 
    ; and turn off the timer closing the socket
+
   ; and turn off the timer closing the socket
    .timer $+ $sockname off
+
  .timer $+ $sockname off
    ; as we can close it now 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)
    ; construct the query to send to the server
+
  ; construct the query to send to the server
    bset &t 1 255 255 255 255 85 255 255 255 255
+
  bset &t 1 255 255 255 255 85 255 255 255 255
    ; open the socket, and ask for a player challenge number
+
  ; open the socket, and ask for a player challenge number
    sockudp -k %sock $1-2 &t
+
  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)
+
  ; 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-*:{
    ; and save it in the binary variable called &reply
+
  ; and save it in the binary variable called &reply
    sockread -f &reply
+
  sockread -f &reply
    ; actually call the alias saved in the sockmark (hlplay_query)
+
  ; actually call the alias saved in the sockmark (hlplay_query)
    $sock($sockname).mark $bvar(&reply,6,4)
+
  $sock($sockname).mark $bvar(&reply,6,4)
    ; and turn the timer off because we can close it manually
+
  ; and turn the timer off because we can close it manually
    .timer $+ $sockname off
+
  .timer $+ $sockname off
    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
+
  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
    bset &query 1 255 255 255 255 85 $3-
+
  bset &query 1 255 255 255 255 85 $3-
    ; use dynamic socket names again
+
  ; use dynamic socket names again
    var %sock = $+(hlplay-,$ticks)
+
  var %sock = $+(hlplay-,$ticks)
    ; open the socket
+
  ; open the socket
    sockudp -k %sock $1-2 &query
+
  sockudp -k %sock $1-2 &query
    ; and turn on the timer closing the socket after 60seconds
+
  ; 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 our query
+
; 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
    sockread -f &reply
+
  sockread -f &reply
    
+
 
    ; set some local variables we will use later
+
   ; set some local variables we will use later
    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 <= %num) {
+
  [[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($bvar(&reply,%offset,128).text) + 1)
+
    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)
+
    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 != $null) {
+
    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
        ; and echo it to the active window with the kills saved above
+
      ; and echo it to the active window with the kills saved above
        echo -a %count $+ . %name - %kills
+
      echo -a %count $+ . %name - %kills
      }
+
    }
 
+
 
      ; increase %i so we go on with the next player
+
    ; increase %i so we go on with the next player
      inc %i
+
    inc %i
    }
+
  }
    
+
 
    ; if there are no players online, echo it aswell
+
   ; if there are no players online, echo it aswell
    if (%count == 0) echo -a No players found!
+
  if (%count == 0) echo -a No players found!
    
+
 
    ; turn off the timer
+
   ; turn off the timer
    .timer $+ $sockname off
+
  .timer $+ $sockname off
    ; and close the socket manually
+
  ; and close the socket manually
    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
}

See also