Counter-Strike and Condition Zero dedicated server on Linux

This isn’t a step-by-step guide, it’s a guideline. I’ve set up a server for Counter-Strike 1.6 (cstrike) and Couter-Strike: Condition Zero (czero) in a virtual machine, using VirtualBox. The big advantage is that it can be exported, copied to another server and imported there and it will (most likely) just run. All the software used is free and completely legal, no license to pay for, the only potential cost is the hosting. Most other Valve/Steam game servers are set up the same way, with different engines having a bit different system requirements, maybe.

1. The Virtual Machine

The idea is to make it as portable as possible. I’ve set it up with each game on a different hard drive, this way I can just copy that drive image to a different machine and it will run with almost no modification required, except maybe things like changing the owner of the files.

That means the machine will have three hard disks attached, one for the OS, the main disk, and the other two for the two game servers running on it. Each disk is a 4 GB disk, dynamically allocated. The whole machine is sitting at around 3.2 GB right now on my server, cstrike takes about 800 MB, czero around 1.2 GB.

The resources I gave it:

  • RAM – 512 MB (just fine for my needs)
  • VRAM – 4 MB (could’ve probably gone even lower, administering is done over ssh, no need for VRAM beyond initial setup)
  • CPU – 1, 100% exec cap, a 1.8 GHz CPU should be enough
  • Hardware Virtulization – enabled
  • Video acceleration -off
  • Network adapter – bridged, this depends on the host. This is the setting that will probably need to be adjusted when moving the machine
  • Audio – disabled
  • USB – disabled. In order to have USB disabled, “Absolute pointing device” needs to also be disabled on System page, if using the GUI to create the machine. This shouldn’t be a problem.
  • Storage – 1 SATA controller, 1 IDE with DVDRom attached. The IDE can be removed after the OS is installed, the SATA will control the hard disks. These can be created now, or attached later.

I have converted the hard disks to writethrough, “write-through hard disks are completely unaffected by snapshots: their state is not saved when a snapshot is taken, and not restored when a snapshot is restored”. I don’t plan on taking snapshots and the normal images tend to get slower over time. Converting the HDDs:

$ VBoxManage list hdds
UUID:        a5cf44aa-90dd-4d7f-81ee-5ad513fd43a5
Parent UUID: base
Format:      VMDK
Location:    /home/vbox/VirtualBox VMs/hlsrv/hlsrv-disk1.vmdk
State:       locked write
Type:        normal
Usage:       hlsrv (UUID: c87250f4-fe68-47ca-a416-6dde5ab07063)
$ VBoxManage modifyhd a5cf44aa-90dd-4d7f-81ee-5ad513fd43a5 --type writethrough

Repeat for all HDDs. Obviously, with the virtual machine shut down.

There’s a potential problem with networking when importing the machine on a new host. Namely, there’s a “feature” of udev on Linux that it will tie a MAC to an interface name (eth0), which means that when importing on a new host if the MAC changes, networking might not be set up properly. The link between MAC and iface is established in /etc/udev/rules.d/70-persistent-net.rules, at least on Debian-like distros. So, there are several options:
1. Before exporting remove all the lines from /etc/udev/rules.d/70-persistent-net.rules, this way even if the MAC changes it doesn’t matter since the old MAC won’t be there to cause trouble.
2. When importing there’s an option in the GUI to not change the MAC. There’s probably a way from command line too, but I’m not aware of it right now.
3. Make a note of the original MAC before exporting and change it after import, either from the GUI or from command line using

VBoxManage modifyvm VMName --macaddressN macaddress

where N is the interface number, starting with 1, i.e.:

VBoxManage modifyvm VMName --macaddress1 0854abdd2349

2. The OS

The only requirement to run the server is GLIBC 2.3.2 or newer. That’s around the year 2003, so pretty much any Linux distro will do. I’ve used a minimal Debian install. The swap partition can be about any size, 512 MB is more than enough, if it starts swaping you’ve got other problems anyway. There should probably be sshd running, other than that it depends on what you want to set up on it.

3. The firewall / port forwarding

Port 27015 is the default port, it can be changed from the command line or server.cfg file.

The client needs:

  • UDP 1200
  • UDP 27000 to 27015
  • TCP 27030 to 27039

4. Installing the server

Official support article (why don’t people link to it in their step-by-step guides?)
Prepare the hard disk, partition it with fdisk(8), install the file system with mkfs(8), mount(8) it. The lines in fstab(5) should look something like:

/dev/sdb1       /cstrike        ext3    rw      0       0
/dev/sdc1       /czero          ext3    rw      0       0

Add a new user, running as root might not be such a good idea. Could add a new user for each game, a user for all games, shell access isn’t strictly needed.

root@hlsrv:/czero# adduser --home /czero czero
Warning: The home dir /czero you specified already exists.
Adding user `czero' ...
Adding new group `czero' (1001) ...
Adding new user `czero' (1001) with group `czero' ...
The home directory `/czero' already exists.  Not copying from `/etc/skel'.
adduser: Warning: The home directory `/czero' does not belong to the user you are currently creating.
Enter new UNIX password: 
Retype new UNIX password: 
passwd: password updated successfully
Changing the user information for czero
Enter the new value, or press ENTER for the default
        Full Name []: 
        Room Number []: 
        Work Phone []: 
        Home Phone []: 
        Other []: 
Is the information correct? [Y/n] y

The home directory can be changed later with usermod(8) -d. Make sure it’s chown(1)-ed to the proper user. Don’t let everyone read config files that have passwords in them, ‘chmod -R o-rwx /czero’ is probably overkill, but at least it’s quick.

Time to install the game. Download hldsupdatetool.bin, make it executable and run it. It will create a new file called ‘steam’. That’s ‘HldsUpdateTool.exe’ from the official knowledge base article, the Linux version. Give it executable permissions and run it like this:

$ ./steam -command update -game czero -dir /czero/czsrv

The first couple of times it will update itself and say something like

Getting version 48 of Steam HLDS Update Tool
Downloading. . . . . . . . . . . .Steam Linux Client updated, please retry the command

keep running it again until it starts downloading. Use

$ ./steam -command list

to see the available games.

That should be it. Probably some addons like AMX should be added to the server, but for now check if it runs:

$ cd /czero/czsrv
$ ./hlds_run -game czero +maxplayers 16 +map de_aztec

5. Addons

Metamod install procedure.
Manual installation of AMX, which is a plugin for metamod that in turn has a lot of plugins written for it.
Non-Steam servers allow clients without Steam to connect. Those are illegal, outdated, harder to administer and all around not worth the trouble considering the price of the games these days. Dproto is a metamod plugin, but no longer maintained, others are payed for in some form. They are dangerous, any of them could be a backdoor. Also, most administration addons use the Steam ID of the player for various tasks, which isn’t available on non-steam clients and that could lead to trouble.

To add admins to AMX Mod X edit the file amxmodx/configs/users.ini, then, client side, add to your autoexec.cfg:

setinfo _pw "password"

where _pw is defined in amxx.cfg, amx_password_field.
Alternatively, from the console, use amx_addadmin.

6. Startup scripts

Tmux is a terminal multiplexer. What this means in practice is that you can start a program inside tmux, “detach” it to leave it running in background, “reattach” the session later from wherever else and see what happened meanwhile. Like screen, but better.
The following shell scripts will start czero and cstrike respectively in two new tmux sessions called “czero” and “cstrike” and detach the session:

cd /czero/czsrv/
tmux new -d -s "czero" "./hlds_run -game czero -nomaster -console"

cd /cstrike/cssrv/
tmux new -d -s "cstrike" "./hlds_run -game cstrike -port 27020 -nomaster -console"

-nomaster will make it so the server isn’t listed on steam.

The scripts should be run as users cstrike and czero. To attach one of the sessions use

$ tmux attach -t cstrike

Detach again with “CTRL+b, d”. The -t parameter isn’t needed if only one session is running as that user.

In order to start the games when the server starts, add to /etc/rc.local:

su - cstrike -c /cstrike/
su - czero -c /czero/

7. Server configuration and optimization

Official optimizing guide.
server.cfg can be found in game’s directory, as in /czero/czsrv/czero/server.cfg:

// Use this file to configure your DEDICATED server. 
// This config file is executed on server start.

// It's a lan server. Usually paired with -nomaster on command line
// so the server isn't listed on steam
sv_lan 1

// disable autoaim
sv_aim 0
sv_cheats 0

// disable clients' ability to pause the server
pausable 0

// default server name. Change to "Bob's Server", etc.
hostname ""
sv_contact ""
// doesn't matter for lan servers, 3 is Europe, -1 for World
sv_region 3

// maximum client movement speed 
sv_maxspeed 320

// server FPS rate, how often it should update the clinet
// value range: 100-1000, default 100
// higher is better, but more CPU intensive
// keep an eye on CPU ('top'), if it constantly goes over 60%, lower this
sys_ticrate 500

// changing these might make clients unable to connect
// defaults should be fine
// allow clients to get maps from server
//sv_allowdownload 1
// do compress them
//sv_filetransfercompression 1
// do not allow clients to upload sprites
//sv_allowupload 0
// limit the maximum size of sprites anyway
//hpk_maxsize .1 MB
// don't send sprites to clients
//sv_send_logos 0
// do send models
//sv_send_resources 1

// 20 minute timelimit on map
mp_timelimit 20

maxplayers 16

// autokick team killers?
mp_autokick 0

// automatically rebalance teams at start of round
mp_autoteambalance 1

// how long is buying allowed, in minutes
//mp_buytime 1.5

// can players use the flashlight?
mp_flashlight 1

// 0 - spectate anyone
// 1 - spectate own team only
// 2 - only see one's dead body
mp_forcechasecam 1

mp_friendlyfire 1

// how long after the round starts until players can move
// in seconds
mp_freezetime 1

// remote console password
rcon_password "rC0nPass"

// load ban files
exec listip.cfg
exec banned.cfg

// set the first map
map de_aztec_cz