Add rcon support

This commit is contained in:
ChillerDragon 2022-11-06 18:26:14 +01:00
parent 2db8b7058d
commit ffbc433b67
7 changed files with 128 additions and 13 deletions

View file

@ -28,4 +28,7 @@ jobs:
./integration_test/run.sh chat ./integration_test/run.sh chat
- name: Test reconnect - name: Test reconnect
run: | run: |
./integration_test/run.sh reconnect ./integration_test/run.sh reconnect
- name: Test rcon
run: |
./integration_test/run.sh rcon

View file

@ -20,5 +20,5 @@ Signal.trap('INT') do
client.disconnect client.disconnect
end end
# connect and detach thread # connect and block main thread
client.connect('localhost', 8303, detach: false) client.connect('localhost', 8303, detach: false)

18
examples/07_rcon.rb Executable file
View file

@ -0,0 +1,18 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require_relative '../lib/teeworlds-client'
client = TeeworldsClient.new
client.on_connected do |_|
client.rcon_auth(password: '123')
client.rcon('shutdown')
end
client.on_rcon_line do |ctx|
puts "[rcon] #{ctx.data[:line]}"
end
# connect and block main thread
client.connect('localhost', 8303, detach: false)

View file

@ -0,0 +1,18 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require_relative '../lib/teeworlds-client'
client = TeeworldsClient.new
client.on_connected do |_|
client.rcon_auth(password: 'rcon')
client.rcon('shutdown')
end
client.on_rcon_line do |ctx|
puts "[rcon] #{ctx.data[:line]}"
end
# connect and block main thread
client.connect('localhost', 8377, detach: false)

View file

@ -3,10 +3,12 @@
cd "$(dirname "$0")" || exit 1 cd "$(dirname "$0")" || exit 1
twbin=teeworlds_srv twbin=teeworlds_srv
rubybin=send_chat_hello.rb
srvcfg='sv_rcon_password rcon;sv_port 8377;killme'
function cleanup() { function cleanup() {
echo "[*] shutting down server ..." echo "[*] shutting down server ..."
pkill -f "$twbin sv_port 8377;killme" pkill -f "$twbin $srvcfg"
[[ "$_timout_pid" != "" ]] && kill "$_timout_pid" &> /dev/null [[ "$_timout_pid" != "" ]] && kill "$_timout_pid" &> /dev/null
} }
@ -15,32 +17,47 @@ trap cleanup EXIT
function timeout() { function timeout() {
local seconds="$1" local seconds="$1"
sleep "$seconds" sleep "$seconds"
pkill -f 'send_chat_hello.rb' pkill -f "$rubybin"
echo "killing: $rubybin"
echo "Error: timeouted" echo "Error: timeouted"
} }
if [[ -x "$(command -v teeworlds_srv)" ]] if [[ -x "$(command -v teeworlds_srv)" ]]
then then
teeworlds_srv "sv_port 8377;killme" &> server.txt & teeworlds_srv "$srvcfg" &> server.txt &
elif [[ -x "$(command -v teeworlds-server)" ]] elif [[ -x "$(command -v teeworlds-server)" ]]
then then
teeworlds-server "sv_port 8377;killme" &> server.txt & teeworlds-server "$srvcfg" &> server.txt &
twbin='teeworlds-server' twbin='teeworlds-server'
elif [[ -x "$(command -v teeworlds-srv)" ]] elif [[ -x "$(command -v teeworlds-srv)" ]]
then then
teeworlds-srv "sv_port 8377;killme" &> server.txt & teeworlds-srv "$srvcfg" &> server.txt &
twbin='teeworlds-srv' twbin='teeworlds-srv'
else else
echo "Error: please install a teeworlds_srv" echo "Error: please install a teeworlds_srv"
exit 1 exit 1
fi fi
timeout 3 killme &
_timout_pid=$!
testname="${1:-chat}" testname="${1:-chat}"
echo "[*] running test '$testname' ..." echo "[*] running test '$testname' ..."
if [ "$testname" == "chat" ]
then
rubybin=send_chat_hello.rb
elif [ "$testname" == "reconnect" ]
then
rubybin=reconnect.rb
elif [ "$testname" == "rcon" ]
then
rubybin=rcon_shutdown.rb
else
echo "Error: unkown test '$testname'"
exit 1
fi
timeout 3 killme &
_timout_pid=$!
function fail() { function fail() {
local msg="$1" local msg="$1"
tail client.txt tail client.txt
@ -51,7 +68,7 @@ function fail() {
if [ "$testname" == "chat" ] if [ "$testname" == "chat" ]
then then
ruby ./send_chat_hello.rb &> client.txt ruby "$rubybin" &> client.txt
if ! grep -q 'hello world' server.txt if ! grep -q 'hello world' server.txt
then then
@ -59,16 +76,24 @@ then
fi fi
elif [ "$testname" == "reconnect" ] elif [ "$testname" == "reconnect" ]
then then
ruby ./reconnect.rb &> client.txt ruby "$rubybin" &> client.txt
if ! grep -q 'bar' server.txt if ! grep -q 'bar' server.txt
then then
fail "Error: did not find 2nd chat message in server log" fail "Error: did not find 2nd chat message in server log"
fi fi
elif [ "$testname" == "rcon" ]
then
ruby "$rubybin" &> client.txt
if pgrep -f "$twbin $srvcfg"
then
fail "Error: server still running rcon shutdown failed"
fi
else else
echo "Error: unkown test '$testname'" echo "Error: unkown test '$testname'"
exit 1 exit 1
fi fi
echo "[+] Test passed client sent chat message to server" echo "[+] Test passed"

View file

@ -106,6 +106,15 @@ class GameClient
@client.send_msg_startinfo @client.send_msg_startinfo
end end
def on_rcon_line(chunk)
u = Unpacker.new(chunk.data[1..])
context = Context.new(
@client,
line: u.get_string
)
@client.hooks[:rcon_line]&.call(context)
end
def on_emoticon(chunk); end def on_emoticon(chunk); end
def on_map_change(chunk) def on_map_change(chunk)

View file

@ -73,6 +73,10 @@ class TeeworldsClient
@hooks[:connected] = block @hooks[:connected] = block
end end
def on_rcon_line(&block)
@hooks[:rcon_line] = block
end
def send_chat(str) def send_chat(str)
@netbase.send_packet( @netbase.send_packet(
NetChunk.create_vital_header({ vital: true }, 4 + str.length) + NetChunk.create_vital_header({ vital: true }, 4 + str.length) +
@ -170,6 +174,42 @@ class TeeworldsClient
@netbase.send_packet(msg, 1) @netbase.send_packet(msg, 1)
end end
def rcon_auth(name, password = nil)
if name.instance_of?(Hash)
password = name[:password]
name = name[:name]
end
if password.nil?
raise "Error: password can not be empty\n" \
" provide two strings: name, password\n" \
" or a hash with the key :password\n" \
"\n" \
" rcon_auth('', '123')\n" \
" rcon_auth(password: '123')\n"
end
data = []
if name.nil? || name == ''
data += Packer.pack_str(password)
else # ddnet auth using name, password and some int?
data += Packer.pack_str(name)
data += Packer.pack_str(password)
data += Packer.pack_int(1)
end
msg = NetChunk.create_vital_header({ vital: true }, data.size + 1) +
[pack_msg_id(NETMSG_RCON_AUTH, system: true)] +
data
@netbase.send_packet(msg, 1)
end
def rcon(command)
data = []
data += Packer.pack_str(command)
msg = NetChunk.create_vital_header({ vital: true }, data.size + 1) +
[pack_msg_id(NETMSG_RCON_CMD, system: true)] +
data
@netbase.send_packet(msg, 1)
end
def send_msg_startinfo def send_msg_startinfo
data = [] data = []
@ -289,6 +329,8 @@ class TeeworldsClient
@game_client.on_connected @game_client.on_connected
when NETMSG_NULL when NETMSG_NULL
# should we be in alert here? # should we be in alert here?
when NETMSG_RCON_LINE
@game_client.on_rcon_line(chunk)
else else
puts "Unsupported system msg: #{chunk.msg}" puts "Unsupported system msg: #{chunk.msg}"
exit(1) exit(1)