I help out a friend of mine out by maintaining his VPS and making sure his Reflex server is up to date. Up until now I have done this manually. The downside of this is that he is all the way over in North America and sometimes an update will be made available in the middle of the night, meaning he has to wait until I wake up. So I decided to write a script to take care of this for me. I just wanted to make sure that I would not kill off a server while he was still on it. He could be in the middle of a match when a new update is pushed and I don't want to boot him off until he leaves the server to go update his client.
First I had a look at how the client queries game servers through the interface in game. So I fired up Wireshark, set it to filter udp queries and had a look at what I sent out. Turns out it was designed to use Source Engine Query, this means we can just do something like this to get some information off the server:
% echo "\xff\xff\xff\xffTSource Engine Query\x00" | socat - udp4:ip:port | hexdump -C
Figured I was set, turns out it was not that easy. The server will utilize two ports, one for game traffic and the other one for steam communication. My friend wanted his server to be private, with the cvar sv_public set to 0. But when the server is no longer public, the port for steam communication is no longer used. I asked the developer about this and he told me to have a look at the title of the window the server creates to figure out how many players are still on. At the time I was not running the server in a graphical environment, but figured I could spare some memory for a small virtual framebuffer.
The script to launch the server:
#!/bin/sh WINEDEBUG=-all HOSTNAME="My Reflex Server" cd $HOME/reflex$1 while true; do xvfb-run \ --auth-file=$HOME/.Xauthority \ --server-num=0 \ --server-args='-screen 0 640x480x16' \ wine reflexded.exe +sv_hostname $HOSTNAME +sv_public 0 sleep 1 done
To avoid cluttering this post with the entire update script, have a look at my gist for the code. It uses a couple of functions to get the job done. Basically it compares the local version to the remote one, if they differ the update function will be called. This function will make sure we find the screen of the framebuffer so that we can query the window for the title. Then we loop until there are 0 players remaining, at which point we finally kill the process off. The loop will make sure it starts up again, assuming the update didn't break anything!