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

From Scriptwiki
Jump to: navigation, search
(update to protocol 48, thanks to NaNg)
m (update by nang)
Line 4: Line 4:
 
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
; usage:
+
  ;
; /hlinfo <ip> <port>
+
  ; usage:
; /hlplay <ip> <port>
+
  ; /hlinfo <ip> <port>
+
  ; or: /hlinfo <ip>:<port>
; the alias that opens a new socket if you want to get general information about a running CS-server
+
   ; /hlplay <ip> <port>
[[alias]] hlinfo {
+
   ; or: /hlplay <ip>:<port>
  ; 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 &t 1 [[$str]]([[$chr]](255),4) [[DollarPlus|$+]] TSource Engine Query
 
  [[bset]] &t 25 0
 
  ; actually open the socket, and send the query
 
  [[sockudp]] -k %sock [[$1-|$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|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]](%ip) + 1)
 
   
 
    ; the same
 
    %name = $bvar(&reply,%offset,128).text
 
    inc %offset $calc($len(%name) + 1)
 
 
    ; the current map
 
    %map = $bvar(&reply,%offset,128).text
 
    inc %offset $calc($len(%map) + 1)
 
 
    ; the game directory
 
    %dir = $bvar(&reply,%offset,128).text
 
    inc %offset $calc($len(%dir) + 1)
 
 
    ; the name of the game
 
    %game = $bvar(&reply,%offset,128).text
 
    inc %offset $calc($len(%game) + 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(%name) + 1)
 
 
    %map = $bvar(&reply,%offset,128).text
 
    inc %offset $calc($len(%map) + 1)
 
 
    %dir = $bvar(&reply,%offset,128).text
 
    inc %offset $calc($len(%dir) + 1)
 
 
    %game = $bvar(&reply,%offset,128).text
 
    inc %offset $calc($len(%game) + 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
+
  ; the alias that opens a new socket if you want to get general information about a running CS-server
  .timer $+ $sockname off
+
  alias hlinfo {
  ; as we can close it now manually
+
    if (!$2) tokenize 58 $1
  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)
 
  ; open the socket, and ask for a player challenge number
 
  sockudp -k %sock $1-2 $str($chr(255),4) $+ U $+ $str($chr(255),4)
 
  ; 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 {
 
  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 [[If-Then-Else#<=|<=]] %num) {
 
    inc %offset
 
 
    
 
    
    ; the name
+
    ; using a dynamic socket name so we can use multiple instances at once
    %name = $bvar(&reply,%offset,128).text
+
    var %sock = $+(hlinfo-,$ticks)
    inc %offset $calc($len(%name) + 1)
+
    ; construct the query to send to the server
+
    bset &t 1 255 255 255 255
    ; the kills
+
    bset -t &t 5 TSource Engine Query
    %kills = $bvar(&reply,%offset).long
+
    bset &t 25 0
    [[If-Then-Else|if]] ([[$isbit]](%kills,32)) [[dec]] %kills $calc(2^32)
+
    ; actually open the socket, and send the query
    inc %offset 8
+
    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))
    ; and if there was a player
+
    .timer $+ %sock 1 60 sockclose %sock
    if (%name [[If-Then-Else#!=|!=]] [[$null]]) {
+
  }
      ; increase the variable that shows us the number of the player
+
 
      inc %count
+
  ; this event will get all data sent back from the server
      ; and echo it to the active window with the kills saved above
+
  on *:UDPREAD:hlinfo-*:{
      echo -a %count $+ . %name - %kills
+
    ; save it in a binary variable
    }
+
    sockread -f &reply
 
+
 
    ; increase %i so we go on with the next player
+
    ; set some local variables that we will use below
    inc %i
+
    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 there are no players online, echo it aswell
+
    if ($chr($bvar(&reply,5)) == m) {
  if (%count [[If-Then-Else#==|==]] 0) echo -a No players found!
+
      ; Half-Life 1 info reply
+
 
  ; turn off the timer
+
      %offset = 6
  .timer $+ $sockname off
+
 
  ; and close the socket manually
+
      ; save the ip
  sockclose $sockname
+
      %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
 +
  }
 +
 
  
 
[[Category:Tutorials]]
 
[[Category:Tutorials]]
 
[[Category:Socket]]
 
[[Category:Socket]]

Revision as of 22:48, 29 January 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://dev.kquery.com/ or http://www.valve-erc.com/srcsdk/Code/Networking/serverqueries.html or 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
 }