Difference between revisions of "How to RCON a source engine based server"

From Scriptwiki
Jump to: navigation, search
m (See also)
m (Fixed some stuff to not override other scripts)
Line 11: Line 11:
 
  ; Usage:
 
  ; Usage:
 
  ; * /sourcercon <ip> <port> <pass>
 
  ; * /sourcercon <ip> <port> <pass>
 
+
 
  ; Define a prefix for sockent names.
 
  ; Define a prefix for sockent names.
 
  [[alias]] -l srcon.prefix { [[return]] SRcon }
 
  [[alias]] -l srcon.prefix { [[return]] SRcon }
 
+
 
  ; Set the command requests and response types.
 
  ; Set the command requests and response types.
 
  [[alias]] -l srcon.req.execcmd { [[return]] 2 }
 
  [[alias]] -l srcon.req.execcmd { [[return]] 2 }
Line 20: Line 20:
 
  [[alias]] -l srcon.res.cmd { [[return]] 0 }
 
  [[alias]] -l srcon.res.cmd { [[return]] 0 }
 
  [[alias]] -l srcon.res.auth { [[return]] 2 }
 
  [[alias]] -l srcon.res.auth { [[return]] 2 }
 
+
 
  ; Turn the given number to a 4-byte number.
 
  ; Turn the given number to a 4-byte number.
 
  [[alias]] -l srcon.inttobytes {
 
  [[alias]] -l srcon.inttobytes {
Line 26: Line 26:
 
   [[return]] [[$1-|$4]] [[$1-|$3]] [[$1-|$2]] [[$1-|$1]]
 
   [[return]] [[$1-|$4]] [[$1-|$3]] [[$1-|$2]] [[$1-|$1]]
 
  }
 
  }
 
+
 
  ; Validates arguments and tries to open the socket to the game server for authentication.
 
  ; Validates arguments and tries to open the socket to the game server for authentication.
 
  [[alias]] sourcercon {
 
  [[alias]] sourcercon {
Line 34: Line 34:
 
     [[return]]
 
     [[return]]
 
   }
 
   }
 
+
 
   ; Save the server's address.
 
   ; Save the server's address.
 
   [[Local_Variables|var]] %server.addr = [[DollarPlus|$+]]([[$1-|$1]],:,[[$1-|$2]])
 
   [[Local_Variables|var]] %server.addr = [[DollarPlus|$+]]([[$1-|$1]],:,[[$1-|$2]])
 
+
 
   ; Check that the user has not opened a Rcon window for the same server already.
 
   ; Check that the user has not opened a Rcon window for the same server already.
 
   ; This makes sure that the user won't have a Rcon step over another Rcon for the same server.
 
   ; This makes sure that the user won't have a Rcon step over another Rcon for the same server.
Line 44: Line 44:
 
     [[return]]
 
     [[return]]
 
   }
 
   }
 
+
 
   ; Open the Rcon window whilst activating it, with an editbox, hide the @ prefix and maximize it.
 
   ; Open the Rcon window whilst activating it, with an editbox, hide the @ prefix and maximize it.
 
   window -aek0x @ [[DollarPlus|$+]] %server.addr
 
   window -aek0x @ [[DollarPlus|$+]] %server.addr
 
   [[aline]] 12 @ [[DollarPlus|$+]] %server.addr Source RCON window. Type a command\cvar to send to the server, responses will be displayed here.
 
   [[aline]] 12 @ [[DollarPlus|$+]] %server.addr Source RCON window. Type a command\cvar to send to the server, responses will be displayed here.
 
   [[aline]] 12 @ [[DollarPlus|$+]] %server.addr Warning! commands like exit or quit will close the server! avoid using them!
 
   [[aline]] 12 @ [[DollarPlus|$+]] %server.addr Warning! commands like exit or quit will close the server! avoid using them!
 
+
 
   ; Set the socket name we'll use.
 
   ; Set the socket name we'll use.
 
   [[Local_Variables|var]] %sockname = [[DollarPlus|$+]]($srcon.prefix,%server.addr)
 
   [[Local_Variables|var]] %sockname = [[DollarPlus|$+]]($srcon.prefix,%server.addr)
 
+
 
   ; Open the socket to the server.
 
   ; Open the socket to the server.
 
   [[sockopen]] %sockname [[$1-|$1]] [[$1-|$2]]
 
   [[sockopen]] %sockname [[$1-|$1]] [[$1-|$2]]
 
+
 
   ; Mark the socket with the Rcon password.
 
   ; Mark the socket with the Rcon password.
 
   [[sockmark]] %sockname [[$1-|$3]]
 
   [[sockmark]] %sockname [[$1-|$3]]
 
  }
 
  }
 
+
 
  ; Catch all sockopens with the Source Rcon sockets prefix.
 
  ; Catch all sockopens with the Source Rcon sockets prefix.
 
  ; Build the authentication request packet and send it to the server.
 
  ; Build the authentication request packet and send it to the server.
Line 65: Line 65:
 
   ; Initialize the request ID for the connection.
 
   ; Initialize the request ID for the connection.
 
   [[set]] %srcon.id. [[DollarPlus|$+]] [[$sockname]] 0
 
   [[set]] %srcon.id. [[DollarPlus|$+]] [[$sockname]] 0
 
+
 
   ; Constructed auth request to send to the server.
 
   ; Constructed auth request to send to the server.
 
   srcon.getrequest &srcon.auth %srcon.id. [ [[DollarPlus|$+]] [ [[$sockname]] ] ] $srcon.req.auth [[$sock]]([[$sockname]]).mark
 
   srcon.getrequest &srcon.auth %srcon.id. [ [[DollarPlus|$+]] [ [[$sockname]] ] ] $srcon.req.auth [[$sock]]([[$sockname]]).mark
 
+
 
   ; Send the authentication packet.
 
   ; Send the authentication packet.
 
   [[sockwrite]] [[$sockname]] &srcon.auth
 
   [[sockwrite]] [[$sockname]] &srcon.auth
 
  }
 
  }
 
+
 
  ; Read data from the game server.
 
  ; Read data from the game server.
 
  on *:SOCKREAD:$([[DollarPlus|$+]]($srcon.prefix, *)): {
 
  on *:SOCKREAD:$([[DollarPlus|$+]]($srcon.prefix, *)): {
 
   ; Get the window name for the current game server.
 
   ; Get the window name for the current game server.
 
   [[Local_Variables|var]] %srcon.window = [[DollarPlus|$+]](@, [[$remove]]([[$sockname]], $srcon.prefix))
 
   [[Local_Variables|var]] %srcon.window = [[DollarPlus|$+]](@, [[$remove]]([[$sockname]], $srcon.prefix))
 
+
 
   ; If there were errors, $sockerr will tell us so.
 
   ; If there were errors, $sockerr will tell us so.
 
   ; Write the error message to the window and clear the connection.
 
   ; Write the error message to the window and clear the connection.
Line 85: Line 85:
 
     [[return]]
 
     [[return]]
 
   }
 
   }
 
+
 
   ; If a prior packet didn't finish receiving, continue reading it.
 
   ; If a prior packet didn't finish receiving, continue reading it.
 
   [[If-Then-Else|if]] ([[$hget]]([[$sockname]])) {
 
   [[If-Then-Else|if]] ([[$hget]]([[$sockname]])) {
 
     ; Set the packet size.
 
     ; Set the packet size.
 
     [[Local_Variables|var]] %srcon.pckt.size = [[$hget]]([[$sockname]], size)
 
     [[Local_Variables|var]] %srcon.pckt.size = [[$hget]]([[$sockname]], size)
 
+
 
     ; Set the number of bytes left to read.
 
     ; Set the number of bytes left to read.
 
     [[Local_Variables|var]] %srcon.pckt.left = [[$hget]]([[$sockname]], left)
 
     [[Local_Variables|var]] %srcon.pckt.left = [[$hget]]([[$sockname]], left)
 
+
 
     ; Set the request id.
 
     ; Set the request id.
 
     [[Local_Variables|var]] %srcon.pckt.id = [[$hget]]([[$sockname]], id)
 
     [[Local_Variables|var]] %srcon.pckt.id = [[$hget]]([[$sockname]], id)
 
+
 
     ; Set the command response type.
 
     ; Set the command response type.
 
     [[Local_Variables|var]] %srcon.pckt.cmd = [[$hget]]([[$sockname]], cmd)
 
     [[Local_Variables|var]] %srcon.pckt.cmd = [[$hget]]([[$sockname]], cmd)
 
+
 
     ; Set the cut string1.
 
     ; Set the cut string1.
 
     [[noop]] [[$hget]]([[$sockname]], string1, &srcon.pckt.string1)
 
     [[noop]] [[$hget]]([[$sockname]], string1, &srcon.pckt.string1)
 
+
 
     ; Read the rest of string1.
 
     ; Read the rest of string1.
 
     [[Sockread|sockread]] %srcon.pckt.left &srcon.pckt.rest
 
     [[Sockread|sockread]] %srcon.pckt.left &srcon.pckt.rest
 
     [[dec]] %srcon.pckt.left $sockbr
 
     [[dec]] %srcon.pckt.left $sockbr
 
+
 
     ; Append the rest of string1 to the already received string1.
 
     ; Append the rest of string1 to the already received string1.
 
     [[bcopy]] &srcon.pckt.string1 [[$calc]]([[$bvar]](&srcon.pckt.string1, 0) + 1) &srcon.pckt.rest 1 -1
 
     [[bcopy]] &srcon.pckt.string1 [[$calc]]([[$bvar]](&srcon.pckt.string1, 0) + 1) &srcon.pckt.rest 1 -1
 
+
 
     ; Clear the un-needed variables.
 
     ; Clear the un-needed variables.
 
     [[hfree]] [[$sockname]]
 
     [[hfree]] [[$sockname]]
Line 118: Line 118:
 
     [[Sockread|sockread]] 4 &srcon.pckt.size
 
     [[Sockread|sockread]] 4 &srcon.pckt.size
 
     [[Local_Variables|var]] %srcon.pckt.size = [[$bvar]](&srcon.pckt.size, 1).long
 
     [[Local_Variables|var]] %srcon.pckt.size = [[$bvar]](&srcon.pckt.size, 1).long
 
+
 
     ; Initialize the number of bytes left to read.
 
     ; Initialize the number of bytes left to read.
 
     [[Local_Variables|var]] %srcon.pckt.left = %srcon.pckt.size
 
     [[Local_Variables|var]] %srcon.pckt.left = %srcon.pckt.size
 
+
 
     ; Read the packet id.
 
     ; Read the packet id.
 
     [[Sockread|sockread]] 4 &srcon.pckt.id
 
     [[Sockread|sockread]] 4 &srcon.pckt.id
 
     [[Local_Variables|var]] %srcon.pckt.id = [[$bvar]](&srcon.pckt.id, 1).long
 
     [[Local_Variables|var]] %srcon.pckt.id = [[$bvar]](&srcon.pckt.id, 1).long
 
     [[dec]] %srcon.pckt.left $sockbr
 
     [[dec]] %srcon.pckt.left $sockbr
 
+
 
     ; If the request id is an error, stop processing, display an error and clear the connection.
 
     ; If the request id is an error, stop processing, display an error and clear the connection.
 
     [[If-Then-Else|if]] ($srcon.inttobytes(%srcon.pckt.id) == 255 255 255 255) {
 
     [[If-Then-Else|if]] ($srcon.inttobytes(%srcon.pckt.id) == 255 255 255 255) {
Line 133: Line 133:
 
       [[return]]
 
       [[return]]
 
     }
 
     }
 
+
 
     ; Read the packet command response type.
 
     ; Read the packet command response type.
 
     [[Sockread|sockread]] 4 &srcon.pckt.cmd
 
     [[Sockread|sockread]] 4 &srcon.pckt.cmd
 
     [[Local_Variables|var]] %srcon.pckt.cmd = [[$bvar]](&srcon.pckt.cmd, 1).long
 
     [[Local_Variables|var]] %srcon.pckt.cmd = [[$bvar]](&srcon.pckt.cmd, 1).long
 
     [[dec]] %srcon.pckt.left $sockbr
 
     [[dec]] %srcon.pckt.left $sockbr
 
+
 
     ; Read string1.
 
     ; Read string1.
 
     [[Sockread|sockread]] %srcon.pckt.left &srcon.pckt.string1
 
     [[Sockread|sockread]] %srcon.pckt.left &srcon.pckt.string1
 
     [[dec]] %srcon.pckt.left $sockbr
 
     [[dec]] %srcon.pckt.left $sockbr
 
   }
 
   }
 
+
 
   ; If the number of bytes left to read is still positive, the packet was splitted.
 
   ; If the number of bytes left to read is still positive, the packet was splitted.
 
   ; This means that we'll save the variables to globals so it can be used in the next read.
 
   ; This means that we'll save the variables to globals so it can be used in the next read.
Line 166: Line 166:
 
       ; Convert the binary string1 to a usable variable.
 
       ; Convert the binary string1 to a usable variable.
 
       [[Local_Variables|var]] %srcon.pckt.string1 = [[$bvar]](&srcon.pckt.string1, 1, [[$bvar]](&srcon.pckt.string1, 0)).text
 
       [[Local_Variables|var]] %srcon.pckt.string1 = [[$bvar]](&srcon.pckt.string1, 1, [[$bvar]](&srcon.pckt.string1, 0)).text
 
+
 
       ; Tokenize the lines to be printed and print them.
 
       ; Tokenize the lines to be printed and print them.
 
       [[tokenize]] [[$asc]]([[$lf]]) %srcon.pckt.string1
 
       [[tokenize]] [[$asc]]([[$lf]]) %srcon.pckt.string1
Line 173: Line 173:
 
   }
 
   }
 
  }
 
  }
 
+
 
  ; Sockets may close on their own if not kept alive.
 
  ; Sockets may close on their own if not kept alive.
 
  ; In case that happens when the window is still open, the socket will be reopened.
 
  ; In case that happens when the window is still open, the socket will be reopened.
Line 179: Line 179:
 
   ; Get the window name for the current game server.
 
   ; Get the window name for the current game server.
 
   [[Local_Variables|var]] %srcon.window = [[DollarPlus|$+]](@, [[$remove]]([[$sockname]], $srcon.prefix))
 
   [[Local_Variables|var]] %srcon.window = [[DollarPlus|$+]](@, [[$remove]]([[$sockname]], $srcon.prefix))
 
+
 
   ; If the window and the current request id still exists, reopen the socket.
 
   ; If the window and the current request id still exists, reopen the socket.
 
   [[If-Then-Else|if]] (([[$window_(remote)|$window(]]%srcon.window)) && (%srcon.id. [ [[DollarPlus|$+]] [ [[$sockname]] ] ])) {
 
   [[If-Then-Else|if]] (([[$window_(remote)|$window(]]%srcon.window)) && (%srcon.id. [ [[DollarPlus|$+]] [ [[$sockname]] ] ])) {
Line 187: Line 187:
 
     %srcon.port = [[$sock]]([[$sockname]]).port
 
     %srcon.port = [[$sock]]([[$sockname]]).port
 
     %srcon.pass = [[$sock]]([[$sockname]]).mark
 
     %srcon.pass = [[$sock]]([[$sockname]]).mark
 
+
 
     ; Terminate the socket so it could be reopened.
 
     ; Terminate the socket so it could be reopened.
 
     [[sockclose]] [[$sockname]]
 
     [[sockclose]] [[$sockname]]
 
+
 
     ; Reopen the socket to the server.
 
     ; Reopen the socket to the server.
 
     [[sockopen]] %srcon.name %srcon.ip %srcon.port
 
     [[sockopen]] %srcon.name %srcon.ip %srcon.port
 
+
 
     ; Mark the socket with the Rcon password.
 
     ; Mark the socket with the Rcon password.
 
     [[sockmark]] %srcon.name %srcon.pass
 
     [[sockmark]] %srcon.name %srcon.pass
 
   }
 
   }
 
  }
 
  }
 
+
 
  ; Sends user input to the server as is.
 
  ; Sends user input to the server as is.
 
  ; NOTE: Commands like exit or quit will close the server!
 
  ; NOTE: Commands like exit or quit will close the server!
Line 204: Line 204:
 
   ; Get the socket name.
 
   ; Get the socket name.
 
   [[Local_Variables|var]] %srcon.socket = [[DollarPlus|$+]]($srcon.prefix, [[$remove]]($target, @))
 
   [[Local_Variables|var]] %srcon.socket = [[DollarPlus|$+]]($srcon.prefix, [[$remove]]($target, @))
 
+
 +
  ; If the input is not for the Source Rcon window, ignore it.
 +
  [[If-Then-Else|if]] ([[$$|$]]([[DollarPlus|$+]](%, srcon.id., %srcon.socket), 2) == [[$null]]) { [[return]] }
 +
 
   ; Check if the socket is still alive.
 
   ; Check if the socket is still alive.
 
   [[If-Then-Else|if]] (![[$sock]](%srcon.socket)) {
 
   [[If-Then-Else|if]] (![[$sock]](%srcon.socket)) {
Line 210: Line 213:
 
     [[return]]
 
     [[return]]
 
   }
 
   }
 
+
 
   ; Build the execute command request packet and send it to the server.
 
   ; Build the execute command request packet and send it to the server.
 
+
 
   ; Increment the current request id.
 
   ; Increment the current request id.
 
   [[inc]] %srcon.id. [[DollarPlus|$+]] %srcon.socket
 
   [[inc]] %srcon.id. [[DollarPlus|$+]] %srcon.socket
 
+
 
   ; Construct the request to send to the game server.
 
   ; Construct the request to send to the game server.
 
   srcon.getrequest &srcon.cmd %srcon.id. [ [[DollarPlus|$+]] [ %srcon.socket ] ] $srcon.req.execcmd [[$1-|$1-]]
 
   srcon.getrequest &srcon.cmd %srcon.id. [ [[DollarPlus|$+]] [ %srcon.socket ] ] $srcon.req.execcmd [[$1-|$1-]]
 
+
 
   [[aline]] $target -> [[$1-|$1-]]
 
   [[aline]] $target -> [[$1-|$1-]]
 
+
 
   ; Send the command request.
 
   ; Send the command request.
 
   [[sockwrite]] %srcon.socket &srcon.cmd
 
   [[sockwrite]] %srcon.socket &srcon.cmd
 
  }
 
  }
 
+
 
  ; If the user has closed the rcon window, clear the socket.
 
  ; If the user has closed the rcon window, clear the socket.
 
  on *:CLOSE:@: {
 
  on *:CLOSE:@: {
 
   srcon.clear [[DollarPlus|$+]]($srcon.prefix, [[$remove]]($target, @))
 
   srcon.clear [[DollarPlus|$+]]($srcon.prefix, [[$remove]]($target, @))
 
  }
 
  }
 
+
 
  ; Constructs a request with the given parameters:
 
  ; Constructs a request with the given parameters:
 
  ; $1 = Bvar to store the request
 
  ; $1 = Bvar to store the request
Line 238: Line 241:
 
   ; Clear the given bvar to make sure it doesn't contain any preset values.
 
   ; Clear the given bvar to make sure it doesn't contain any preset values.
 
   [[bunset]] [[$1-|$1]]
 
   [[bunset]] [[$1-|$1]]
 
+
 
   ; Set the given request ID in the request.
 
   ; Set the given request ID in the request.
 
   [[bset]] [[$1-|$1]] 5 $srcon.inttobytes([[$1-|$2]])
 
   [[bset]] [[$1-|$1]] 5 $srcon.inttobytes([[$1-|$2]])
 
+
 
   ; Set the given request type.
 
   ; Set the given request type.
 
   [[bset]] [[$1-|$1]] 9 $srcon.inttobytes([[$1-|$3]])
 
   [[bset]] [[$1-|$1]] 9 $srcon.inttobytes([[$1-|$3]])
 
+
 
   ; Set string1 the request data.
 
   ; Set string1 the request data.
 
   [[bset]] -t [[$1-|$1]] 13 [[$1-|$4-]]
 
   [[bset]] -t [[$1-|$1]] 13 [[$1-|$4-]]
 
+
 
   ; Terminate string1 with 0x00 and set string2 to 0x00.
 
   ; Terminate string1 with 0x00 and set string2 to 0x00.
 
   [[bset]] [[$1-|$1]] [[$calc]]([[$bvar]]([[$1-|$1]],0) + 1) 00 00
 
   [[bset]] [[$1-|$1]] [[$calc]]([[$bvar]]([[$1-|$1]],0) + 1) 00 00
 
+
 
   ; Set the packet size.
 
   ; Set the packet size.
 
   [[bset]] [[$1-|$1]] 1 $srcon.inttobytes([[$calc]]([[$bvar]]([[$1-|$1]], 0) - 4))
 
   [[bset]] [[$1-|$1]] 1 $srcon.inttobytes([[$calc]]([[$bvar]]([[$1-|$1]], 0) - 4))
 
  }
 
  }
 
+
 
  ; Clears the socket and the request id.
 
  ; Clears the socket and the request id.
 
  [[alias]] -l srcon.clear {
 
  [[alias]] -l srcon.clear {
Line 261: Line 264:
 
     [[sockclose]] [[$1-|$1]]
 
     [[sockclose]] [[$1-|$1]]
 
   }
 
   }
 
+
 
   ; Clear the id var.
 
   ; Clear the id var.
 
   [[Unset|unset]] %srcon.id. [[DollarPlus|$+]] [[$1-|$1]]
 
   [[Unset|unset]] %srcon.id. [[DollarPlus|$+]] [[$1-|$1]]
Line 267: Line 270:
  
 
==See also==
 
==See also==
 +
* [[How to RCON a HL1 engine based server]]
 
* [[How to query a CS Server]]
 
* [[How to query a CS Server]]
 
* [[How to parse HL log data]]
 
* [[How to parse HL log data]]

Revision as of 20:38, 11 June 2011

; Source RCON control snippet by NaNg
; Date: 12-02-2011
; Tested on mIRC 7.14
;
; Remote control window for Source engine based games (e.g. HL2, OrangeBox, CS:S, CSP etc.),
; based on the Source RCON Protocol found at http://developer.valvesoftware.com/wiki/Source_RCON_Protocol
;
; Warning! I couldn't find a perfect way to open the socket nicely everytime it closes, so don't rely on it
; to be connected 24/7 (plus, the socket is closed on the server side every changelevel).
;
; Usage:
; * /sourcercon <ip> <port> <pass>

; Define a prefix for sockent names.
alias -l srcon.prefix { return SRcon }

; Set the command requests and response types.
alias -l srcon.req.execcmd { return 2 }
alias -l srcon.req.auth { return 3 }
alias -l srcon.res.cmd { return 0 }
alias -l srcon.res.auth { return 2 }

; Turn the given number to a 4-byte number.
alias -l srcon.inttobytes {
  tokenize 46 $longip($1)
  return $4 $3 $2 $1
}

; Validates arguments and tries to open the socket to the game server for authentication.
alias sourcercon {
  ; Validate arguments (simple, no need for full validation).
  if ((!$longip($1)) || ($2 !isnum 1-65536) || (!$3)) {
    echo -ag Invalid parameters.
    return
  }

  ; Save the server's address.
  var %server.addr = $+($1,:,$2)

  ; Check that the user has not opened a Rcon window for the same server already.
  ; This makes sure that the user won't have a Rcon step over another Rcon for the same server.
  if ($window(%server.addr)) {
    echo -ag Rcon window for $1 is already open! close it before trying.
    return
  }

  ; Open the Rcon window whilst activating it, with an editbox, hide the @ prefix and maximize it.
  window -aek0x @ $+ %server.addr
  aline 12 @ $+ %server.addr Source RCON window. Type a command\cvar to send to the server, responses will be displayed here.
  aline 12 @ $+ %server.addr Warning! commands like exit or quit will close the server! avoid using them!

  ; Set the socket name we'll use.
  var %sockname = $+($srcon.prefix,%server.addr)

  ; Open the socket to the server.
  sockopen %sockname $1 $2

  ; Mark the socket with the Rcon password.
  sockmark %sockname $3
}

; Catch all sockopens with the Source Rcon sockets prefix.
; Build the authentication request packet and send it to the server.
on *:SOCKOPEN:$($+($srcon.prefix, *)): {
  ; Initialize the request ID for the connection.
  set %srcon.id. $+ $sockname 0

  ; Constructed auth request to send to the server.
  srcon.getrequest &srcon.auth %srcon.id. [ $+ [ $sockname ] ] $srcon.req.auth $sock($sockname).mark

  ; Send the authentication packet.
  sockwrite $sockname &srcon.auth
}

; Read data from the game server.
on *:SOCKREAD:$($+($srcon.prefix, *)): {
  ; Get the window name for the current game server.
  var %srcon.window = $+(@, $remove($sockname, $srcon.prefix))

  ; If there were errors, $sockerr will tell us so.
  ; Write the error message to the window and clear the connection.
  if ($sockerr) {
    aline 04 %srcon.window * Error: Reading from socket: $sock($sockname).wsmsg
    srcon.clear $sockname
    return
  }

  ; If a prior packet didn't finish receiving, continue reading it.
  if ($hget($sockname)) {
    ; Set the packet size.
    var %srcon.pckt.size = $hget($sockname, size)

    ; Set the number of bytes left to read.
    var %srcon.pckt.left = $hget($sockname, left)

    ; Set the request id.
    var %srcon.pckt.id = $hget($sockname, id)

    ; Set the command response type.
    var %srcon.pckt.cmd = $hget($sockname, cmd)

    ; Set the cut string1.
    noop $hget($sockname, string1, &srcon.pckt.string1)

    ; Read the rest of string1.
    sockread %srcon.pckt.left &srcon.pckt.rest
    dec %srcon.pckt.left $sockbr

    ; Append the rest of string1 to the already received string1.
    bcopy &srcon.pckt.string1 $calc($bvar(&srcon.pckt.string1, 0) + 1) &srcon.pckt.rest 1 -1

    ; Clear the un-needed variables.
    hfree $sockname
  }
  ; Else, if the packet wasn't splitted.
  else {
    ; Read the packet size.
    sockread 4 &srcon.pckt.size
    var %srcon.pckt.size = $bvar(&srcon.pckt.size, 1).long

    ; Initialize the number of bytes left to read.
    var %srcon.pckt.left = %srcon.pckt.size

    ; Read the packet id.
    sockread 4 &srcon.pckt.id
    var %srcon.pckt.id = $bvar(&srcon.pckt.id, 1).long
    dec %srcon.pckt.left $sockbr

    ; If the request id is an error, stop processing, display an error and clear the connection.
    if ($srcon.inttobytes(%srcon.pckt.id) == 255 255 255 255) {
      aline 04 %srcon.window * Error: Failed authentication (probably wrong password).
      srcon.clear $sockname
      return
    }

    ; Read the packet command response type.
    sockread 4 &srcon.pckt.cmd
    var %srcon.pckt.cmd = $bvar(&srcon.pckt.cmd, 1).long
    dec %srcon.pckt.left $sockbr

    ; Read string1.
    sockread %srcon.pckt.left &srcon.pckt.string1
    dec %srcon.pckt.left $sockbr
  }

  ; If the number of bytes left to read is still positive, the packet was splitted.
  ; This means that we'll save the variables to globals so it can be used in the next read.
  if (%srcon.pckt.left > 0) {
    hmake $sockname
    hadd $sockname size %srcon.pckt.size
    hadd $sockname left %srcon.pckt.left
    hadd $sockname id %srcon.pckt.id
    hadd $sockname cmd %srcon.pckt.cmd
    hadd -b $sockname string1 &srcon.pckt.string1
  }
  ; Only if we've finished reading all the packet, display it.
  ; NOTE: At this point, string1 also contains string2 (the one 0x00 byte), however it doesn't matter to us
  ; since we're only reading the text which is terminated in string1 by a 0x00 byte.
  elseif (%srcon.pckt.left == 0) {
    ; If the response type was an auth response, display a "connected" message.
    if (%srcon.pckt.cmd == $srcon.res.auth) {
      aline 12 %srcon.window Now connected!
    }
    ; If the response type was a command response, display it.
    elseif (%srcon.pckt.cmd == $srcon.res.cmd) {
      ; Convert the binary string1 to a usable variable.
      var %srcon.pckt.string1 = $bvar(&srcon.pckt.string1, 1, $bvar(&srcon.pckt.string1, 0)).text

      ; Tokenize the lines to be printed and print them.
      tokenize $asc($lf) %srcon.pckt.string1
      aline %srcon.window $*
    }
  }
}

; Sockets may close on their own if not kept alive.
; In case that happens when the window is still open, the socket will be reopened.
on *:SOCKCLOSE:$($+($srcon.prefix, *)): {
  ; Get the window name for the current game server.
  var %srcon.window = $+(@, $remove($sockname, $srcon.prefix))

  ; If the window and the current request id still exists, reopen the socket.
  if (($window(%srcon.window)) && (%srcon.id. [ $+ [ $sockname ] ])) {
    ; Save the socket details before terminating it.
    %srcon.name = $sockname
    %srcon.ip = $sock($sockname).ip
    %srcon.port = $sock($sockname).port
    %srcon.pass = $sock($sockname).mark

    ; Terminate the socket so it could be reopened.
    sockclose $sockname

    ; Reopen the socket to the server.
    sockopen %srcon.name %srcon.ip %srcon.port

    ; Mark the socket with the Rcon password.
    sockmark %srcon.name %srcon.pass
  }
}

; Sends user input to the server as is.
; NOTE: Commands like exit or quit will close the server!
on *:INPUT:@: {
  ; Get the socket name.
  var %srcon.socket = $+($srcon.prefix, $remove($target, @))

  ; If the input is not for the Source Rcon window, ignore it.
  if ($($+(%, srcon.id., %srcon.socket), 2) == $null) { return }

  ; Check if the socket is still alive.
  if (!$sock(%srcon.socket)) {
    aline 04 $target * Error: Socket is dead. Close the window and try again.
    return
  }

  ; Build the execute command request packet and send it to the server.

  ; Increment the current request id.
  inc %srcon.id. $+ %srcon.socket

  ; Construct the request to send to the game server.
  srcon.getrequest &srcon.cmd %srcon.id. [ $+ [ %srcon.socket ] ] $srcon.req.execcmd $1-

  aline $target -> $1-

  ; Send the command request.
  sockwrite %srcon.socket &srcon.cmd
}

; If the user has closed the rcon window, clear the socket.
on *:CLOSE:@: {
  srcon.clear $+($srcon.prefix, $remove($target, @))
}

; Constructs a request with the given parameters:
; $1 = Bvar to store the request
; $2 = Request ID
; $3 = Request type
; $4- = Request data (password/command)
alias -l srcon.getrequest {
  ; Clear the given bvar to make sure it doesn't contain any preset values.
  bunset $1

  ; Set the given request ID in the request.
  bset $1 5 $srcon.inttobytes($2)

  ; Set the given request type.
  bset $1 9 $srcon.inttobytes($3)

  ; Set string1 the request data.
  bset -t $1 13 $4-

  ; Terminate string1 with 0x00 and set string2 to 0x00.
  bset $1 $calc($bvar($1,0) + 1) 00 00

  ; Set the packet size.
  bset $1 1 $srcon.inttobytes($calc($bvar($1, 0) - 4))
}

; Clears the socket and the request id.
alias -l srcon.clear {
  ; If there's a socket named accordingly, close it.
  if ($sock($1)) {
    sockclose $1
  }

  ; Clear the id var.
  unset %srcon.id. $+ $1
}

See also