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

From Scriptwiki
Jump to: navigation, search
(doesnt work + weird text - needs rewriting perhaps)
 
Line 1: Line 1:
{{stub}}
+
If you want to e.g. get the umber 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.
 
 
If you want to e.g. get the umber 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/.
 
  
  
 
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 (completly) 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 (completly) to be able to use it.
  
  ; this is a handy alias to call the script. It will call the "hlx" alias with an ip and a port as argument
+
  ; HL server query snippet by Saturn
  ; you can add more aliases like this one.
+
  ;
  ; if you want to all "hlq" directly, you can use something like: "/hlq <ip> <port>"
+
  ; usage:
  [[alias]] hlx { hlq <ip> <port> }
+
; /hlinfo <ip> <port>
 +
  ; /hlplay <ip> <port>
 
   
 
   
  ; the main alias to open a new socket connections
+
  ; the alias that opens a new socket if you want to get general information about a running CS-server
  ; syntax: /hlq <ip> <port>
+
  [[alias]] hlinfo {
alias hlq {
+
  ; using a dynamic socket name so we can use multiple instances at once
   [[sockudp]] -k hlq [[$1]]-2 [[DollarPlus|$+]]([[$str]]($chr(255),4),players)
+
  [[var]] %sock = $+(hlinfo-,$ticks)
 +
  ; actually open the socket
 +
   [[sockudp]] -k %sock [[$1]]-2 [[$str]]([[$chr]](255),4) [[DollarPlus|$+]] TSource Engine Query
 +
  ; 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 read every data from the cs server
+
  ; this event will get all data send back from the server
  on *:udpread:hlq*:{
+
  on *:udpread:hlinfo-*:{
   ; we put it in a binary variable
+
   ; save it in a binary variable
   [[sockread]] -f &data
+
   [[sockread]] -f &reply
 +
 +
  ; set some local variables that we will use below
 +
  var %offset, %name, %map, %game, %num, %max, %ip, %dir
 
    
 
    
   ; the sixth byte is the number of players, so we set it to a variable
+
   ; following the protocol, this "m" shows us that we did query a Half-Life 1 server
   [[var]] %players = $bvar(&data, 6, 1)
+
   if ($chr($bvar(&reply,5)) == m) {
  var %offset = 7
+
    ; Half-Life 1 info reply
 +
   
 +
    %offset = 6
 
    
 
    
   ; that's a variable for the while loop some lines below
+
    ; saving the ip
  var %d = 1
+
    %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))
 +
  }
 
    
 
    
   ; return a line seperator (usually "-")
+
   ; now we echo all stored details to the active window
   [[linesep]]
+
  echo -a Info for $sock($sockname).saddr $+ : $+ $sock($sockname).sport
   ; echo the number of players
+
  echo -a Name: %name
   [[echo]] -a %players Players
+
  echo -a Map: %map
 +
  echo -a Game: %game
 +
  echo -a Players: %num $+ / $+ %max
 +
 
 +
  ; and turn the timer to close the socket off
 +
  .timer $+ $sockname off
 +
  ; as we can close it manually now
 +
  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
 +
  sockudp -k %sock $1-2 $str($chr(255),4) $+ W
 +
  ; 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 the alias to close the socket after 60seconds on
 +
  .timer $+ %sock 1 60 sockclose %sock
 +
}
 +
 +
; this event will receive all data from the server as response to out 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
 
    
 
    
  ; now we are looping through all players to get some stats about them
+
     ; the name
  [[while]] (%d <= %players) {
+
     %name = $bvar(&reply,%offset,128).text
    ; the following lines are getting these stats. If you want to learn more about this, take a look
 
    ; at http://dev.kquery.com/
 
   
 
    ; we get the playernumber
 
    var %playernum = $bvar(&data, %offset, 1)
 
    [[inc]] %offset
 
   
 
     ; the name of the player
 
     var %name = $bvar(&data, %offset, 200).text
 
 
     inc %offset $calc($len(%name) + 1)
 
     inc %offset $calc($len(%name) + 1)
   
+
     ; the current score
+
     ; the kills
     var %score = $bvar(&data, %offset, 4)
+
     %kills = $bvar(&reply,%offset).long
     inc %offset 4
+
    if ($isbit(%kills,32)) dec %kills $calc(2^32)
      
+
     inc %offset 8
     inc %offset 4
+
   
+
     ; and if there was a player
    ; and echo everything
+
     if (%name != $null) {
    echo -a %playernum - name: %name - score: $be2le(%score)
+
      ; 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 the variable for the while loop to get stats about the next player
+
     ; increase %i so we go on with the next player
     inc %d
+
     inc %i
 
   }
 
   }
 
    
 
    
   ; at the end, we are closing the socket to be able to open a new one later
+
   ; if there are no players online, echo is aswell
 +
  if (%count == 0) echo -a No players found!
 +
 +
  ; turn off the timer
 +
  .timer $+ $sockname off
 +
  ; and close the socket manually
 
   sockclose $sockname
 
   sockclose $sockname
}
 
; this alias converts 'big endians' into 'little endians'
 
alias be2le {
 
  tokenize 32 $1 | return $calc($1 + $2 * 2^8 + $4 * 2^16 + $3 * 2^24)
 
 
  }
 
  }

Revision as of 15:03, 14 April 2006

If you want to e.g. get the umber 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.


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 (completly) to be able to use it.

; HL server query snippet by Saturn
;
; usage:
; /hlinfo <ip> <port>
; /hlplay <ip> <port>

; the alias that opens a new socket if you want to get general information about a running CS-server
alias hlinfo {
 ; using a dynamic socket name so we can use multiple instances at once
 var %sock = $+(hlinfo-,$ticks)
 ; actually open the socket
 sockudp -k %sock $1-2 $str($chr(255),4) $+ TSource Engine Query
 ; 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 send 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 did query a Half-Life 1 server
 if ($chr($bvar(&reply,5)) == m) {
   ; Half-Life 1 info reply
   
   %offset = 6
 
   ; saving 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 the timer to close the socket off
 .timer $+ $sockname off
 ; as we can close it manually now
 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
 sockudp -k %sock $1-2 $str($chr(255),4) $+ W 
 ; 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 the alias to close the socket after 60seconds on
 .timer $+ %sock 1 60 sockclose %sock
}

; this event will receive all data from the server as response to out 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(%name) + 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 is aswell
 if (%count == 0) echo -a No players found!

 ; turn off the timer
 .timer $+ $sockname off
 ; and close the socket manually
 sockclose $sockname
}