How to RCON an HL1 engine based server

From Scriptwiki
Jump to: navigation, search
; HL1 RCON control snippet by NaNg
; Date: 11-06-2011
; Tested on mIRC 7.19
;
; Remote control window for HL1 engine based games (e.g. HL, CS, TF etc.),
; based on the HL1 Rcon Protocol (no source).
;
; Usage:
; * /hlrcon <ip> <port> <pass>

; Define a prefix for sockent names.
alias -l hlrcon.prefix { return HLRcon }

; Validates arguments and challenges the game server.
alias hlrcon {
  ; 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 HL 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 = $+($hlrcon.prefix, %server.addr)

  ; Save the Rcon password.
  set $+(%, $hlrcon.prefix, .rconpw., %sockname) $3

  ; Challenge the Rcon.
  hlrcon.challengercon %sockname $1 $2
}

alias -l hlrcon.challengercon {
  ; Construct the challenge request query.
  bset &hlrcon.challenge 1 255 255 255 255
  bset -t &hlrcon.challenge 5 challenge rcon

  ; Open the socket and challenge the game server.
  sockudp -k $$1-3 &hlrcon.challenge
}

; Read data from the game server.
on *:UDPREAD:$($+($hlrcon.prefix, *)): {
  ; Get the window name for the current game server.
  var %hlrcon.window = $+(@, $remove($sockname, $hlrcon.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 %hlrcon.window * Error: Reading from socket: $sock($sockname).wsmsg
    sockclose $sockname
    return
  }

  ; Read the data packet type.
  sockread 4 &hlrcon.pckt.type

  ; Since we are not using it (and we are disregarding longer packets), drop the packet type.
  bunset &hlrcon.pckt.type

  ; Read the rest of the data.
  sockread 1400 &hlrcon.pckt.data
  var %hlrcon.data = $bvar(&hlrcon.pckt.data, 1, $bvar(&hlrcon.pckt.data, 0)).text

  ; If Rcon challenge is received, set it.
  if (challenge rcon* iswm %hlrcon.data) {
    ; Tokenize the received data to rows.
    tokenize $asc($lf) %hlrcon.data

    ; Tokenize the first row to get the challenge.
    tokenize 32 $1

    ; Save the challenge.
    set $+(%, $hlrcon.prefix, .challenge., $sockname) $3
  }
  else {
    ; Ignore the first byte (for non-log responses, the response contains this extra non-sense byte).
    var %hlrcon.data = $bvar(&hlrcon.pckt.data, 2, $bvar(&hlrcon.pckt.data, 0)).text
  }

  ; Tokenize the received data to rows.
  tokenize $asc($lf) %hlrcon.data

  ; Dispose the bvar.
  bunset &hlrcon.pckt.data

  ; Print the given data.
  aline %hlrcon.window $*

  ; If an error occured, display a friendly message.
  if (bad challenge* iswm $1) {
    hlrcon.challengercon $sockname $sock($sockname).addr $sock($sockname).port
    aline 04 %hlrcon.window Bad challenge, requesting a new one. Please try again.
  }
  elseif (bad rcon_password* iswm $1) {
    aline 04 %hlrcon.window Bad Rcon password. Please try again.
  }
}

; 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 %hlrcon.socket = $+($hlrcon.prefix, $remove($target, @))

  ; If the input is not for the HL Rcon window, ignore it.
  if ($($+(%, $hlrcon.prefix, .rconpw., %hlrcon.socket), 2) == $null) { return }

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

  ; Build the command packet and send it to the server.
  bset &hlrcon.cmd 1 255 255 255 255
  bset -t &hlrcon.cmd 5 rcon $($+(%, $hlrcon.prefix, .challenge., %hlrcon.socket), 2) $+(", $($+(%, $hlrcon.prefix, .rconpw., %hlrcon.socket), 2), ") $1-

  aline $target -> $1-

  ; Send the command request.
  sockudp -k %hlrcon.socket $sock(%hlrcon.socket).saddr $sock(%hlrcon.socket).sport &hlrcon.cmd
}

; If the user has closed the HL Rcon window, clear the socket.
on *:CLOSE:@: {
  ; Construct the sock name.
  var %hlrcon.socket = $+($hlrcon.prefix, $remove($target, @))

  ; If the input is not for the HL Rcon window, ignore it.
  if ($($+(%, $hlrcon.prefix, .rconpw., %hlrcon.socket), 2) == $null) { return }

  ; If there's a socket named accordingly, close it.
  if ($sock(%hlrcon.socket)) {
    sockclose %hlrcon.socket
  }

  ; Clear any variables set for this socket.
  unset $($+(%, $hlrcon.prefix, .*., %hlrcon.socket))
}

See also