In this post I'll be covering some of my methods for debugging and testing internal Source projects. This is the first part where we'll mainly focus on setting up a dedicated game server for online testing. Later on we'll dive into the game libraries where I'll show you how to find various classes and pointers with gdb.

Launching and options

The few months I've spent developing on Linux have been an iterative process. I used to start the game normally through Steam but recently I've started using the launch script directly. Now I can quickly switch between launch options without touching the Steam interface, start the game with gdb by setting the DEBUGGER environment variable, use stdout and stderr for printing debugging text, consistently unload my libraries without touching LD_PRELOAD and so on.

Right now my regular launch options look like this. It opens the game in a 1024x768 window with the developer console open, skips the introduction videos and disables crash dump uploading. If you need to join a secure server you can add the '-steam' option which enables Valve Anti-Cheat — a requirement for participating in matchmaking and most community servers.

./ -sw -w 1024 -h 768 -novid -console -nobreakpad +sv_lan 1
— "Normal" launching options

For simplicity's sake, you might want to make a symbolic link from the game folder to something like '~/csgo'.

Dedicated servers

Testing online is really important. Looking back, it's probably the one thing I neglected the most when I first started with internal coding. What works on an offline listen server won't necessarily work as well or at all online.

I've briefly covered Steamcmd usage in this post so you'll only need to swap the application ID in that tutorial with 740.

For some extra functionality, consider installing Metamod:Source and SourceMod - at the moment I'm using it for the Auto SourceTV Recorder and Instant Respawning plugins. Base installation is as simple as extracting both .tar.gz archives over the csgo folder and adding your Steam ID to admins_simple.ini in addons/sourcemod/configs.

Installing and configuring the dedicated server. (mirror)

First off, we'll need a server.cfg file which'll be executed every time the server loads a new map.

sv_lan 1
sv_cheats 1
sv_grenade_trajectory 1
sv_infinite_ammo 2
sv_airaccelerate 150
sv_enablebunnyhopping 1
sv_hibernate_when_empty 0

I usually do all my testing alone so I use sv_lan to restrict incoming connections to my local network - this also allows me to stay connected to the server despite the authenticaton failures due to Steam offline mode. Cheats are enabled to allow quick navigation with noclip and usage of ConVars such as sv_grenade_trajectory.

Infinite ammo is enabled in the second mode meaning you'll still reload but never run out of ammo - you can either disable this or set it to 1 so you'll never have to reload. Bunnyhopping is enabled and air acceleration is turned up for easier air strafing.

tv_enable 1
tv_delay 0
tv_snapshotrate 64

SourceTV is enabled here which allows you to spectate and record yourself from the perspective of the server. The default delay is disabled so we have the option to watch in real-time from another machine.

For a more realistic recording we set the snapshot tickrate to match that of the server. 64-tick is the default used in official matchmaking while 128-tick is commonly used in third-party leagues and some community servers.

bot_stop 1
bot_join_after_player 0

Finally, all bot AI is disabled and they are set to join the game before we do. Now when we connect we'll be in a game with 4 teammates and 5 enemies that won't move at all. You can run 'toggle bot_stop' to switch the bot AI on and off.

Naturally, you'll need to enter commands and set variables on the server console now! If you installed SourceMod simply prepend 'sm_rcon' and it'll run on the server. Otherwise you'll need to switch to the server window and type it there. Source also has built-in RCON but I won't go into that here since there are potential issues that are beyond the scope of this post.

There's still a few more things to do. Configurations in Counter-Strike: Global Offensive are a little different to other Source games. Game modes such as Classic Casual and Classic Competitive are split up into separate configurations which are loaded at a different time to server.cfg. Unfortunately, that means we can't just override everything we want in server.cfg, we'll need to make a gamemode_competitive_server.cfg file as well to override the default values.

mp_autokick 0
mp_buytime 999999
mp_maxrounds 999999
mp_buy_anywhere 1
mp_drop_knife_enable 1
mp_freezetime 0
mp_halftime 0
mp_roundtime 60
mp_roundtime_hostage 60
mp_roundtime_defuse 60
mp_startmoney 16000
mp_respawn_on_death_t 1
mp_respawn_on_death_ct 1
mp_do_warmup_offine 0
mp_do_warmup_period 0
mp_warmuptime 0

The extra configuration above extends buy time, start money, round time and maximum amount of rounds, allows you to use the buy menu anywhere, enables knife dropping, disables freeze time, half time, warmup and auto-kick for idling or team-killing and enables respawning. Now that everything is configured we can finally start the server!

I've uploaded both configuration files here that you can directly extract to the 'csgo/cfg' folder. I recommend also installing the aim_ag_texture2 custom map since it loads a lot faster than any official map.

./srcds_run -game csgo -insecure +game_mode 1 +map aim_ag_texture2

You can now connect from the community server browser or by typing 'connect' in Console. If you've noticed that some of your features have immediately stopped working then you've got some work to do! It's also worth testing in poor network conditions which can be emulated using the net_fakelag and net_fakeloss commands.

If you're testing a feature such as silent aim which sends different view angles to the server than the ones reflected on the client you may want to record a SourceTV demo to see your view angles from another perspective. If you installed SourceMod and the previously mentioned Auto Recorder plugin you can simply use sm_record and sm_stoprecord which will automatically start recording with a timestamped filename. If not, you can use tv_record demoname and tv_stoprecord instead.

You can then take the resulting .dem files from the server and copy them to your client csgo folder. Once copied, you can activate the playback interface by pressing Shift+F2 or by typing demoui in Console.

Compiler options

During development you'll want to disable compiler optimisations and enable debugging information. This way, you'll have the most meaningful backtraces when something inevitably goes wrong. This part isn't game specific so I'll be using Counter-Strike: Source with my cstrike-basehook-linux project as an example.

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -ggdb -std=c++14 -m32 -Wall -Wextra -Wpedantic")

After you've re-compiled and loaded your library, you can attach gdb, continue and wait for the game to crash. When it does, switch back to the debugger console and type backtrace to see where the crash occurred.

For example, below is a backtrace after I shifted the virtual index of IBaseClientDLL::GetAllClasses by one. You can see the CreateMove hook calling GetFlags on the LocalPlayer entity for the auto-bunnyhop feature. As this tries to look up the offset of 'm_fFlags' the game throws a segmentation fault.

$ sudo gdb attach $(pidof hl2_linux)
(gdb) continue
Thread 1 "hl2_linux" received signal SIGSEGV, Segmentation fault.
0xda555b81 in NetVars::GetOffset (class_name=0xda62aaff "CBasePlayer", 
    property_name=0xda62aaf6 "m_fFlags", property_ptr=0x0)
    at /home/aixxe/Projects/cstrike-basehook-linux/src/Hooks/../Utilities/NetVars.h:33
33          if (strcmp(class_ptr->m_pNetworkName, class_name) == 0) {
(gdb) backtrace
#0  0xda555b81 in NetVars::GetOffset (class_name=0xda62aaff "CBasePlayer", 
    property_name=0xda62aaf6 "m_fFlags", property_ptr=0x0)
    at /home/aixxe/Projects/cstrike-basehook-linux/src/Hooks/../Utilities/NetVars.h:33
#1  0xda555bf1 in C_BasePlayer::GetFlags (this=0x20334800)
    at /home/aixxe/Projects/cstrike-basehook-linux/src/Hooks/../Game/Entity.h:9
#2  0xda55566d in Hooks::CreateMove (thisptr=0xe922c144, sequence=103, frametime=0.00393634662, 
    active=true) at /home/aixxe/Projects/cstrike-basehook-linux/src/Hooks/CreateMove.cpp:25

Once you're ready to play on secure servers or release your library to others you can turn the optimisations on and drop the debug information. This'll make your library a lot smaller in size and should increase overall performance.

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -s -std=c++14 -m32 -Wall -Wextra -Wpedantic")

That's all for now, a little short but I'm trying to keep to my arbitrary deadlines for these posts. I hope that last part with gdb was interesting because there's a lot more where that came from in the future. Happy new year!

Last updated Friday, 17 January 2020 at 07:18 PM.