Programming is hard, and programming with the Half-Life SDK is harder. Sooner or later, something will go wrong, and you'll have no idea why. What can you do about that? The answer is, of course,
debugging!
Getting Half-Life to debug properly takes a little bit of set up, so this article will guide you through what you need to do to get everything working.
This isn't a tutorial on how to use Visual Studio or GDB - only how to set up the Half-Life SDK for those tools. For information on how to use the debuggers, take a look here:
Debugging on Windows using Visual Studio
This method is recommended for most users, as VS makes debugging very convenient and easy. Assuming you have VS already set up and your mod is running, there's two extra things you need to do to get debugging working properly.
Copy DLL files on build
In Visual Studio, right click on your
hldll project and select
Properties. Expand
Build Events and select
Post-Build Event. The easiest way to deal with this is to get the post-build event to run a custom batch file on completion. If you're using the
halflife-updated
repository, enter this script into the
Command Line option (drop down the option and select
<Edit...> for a multi-line editor):
cd /D "$(ProjectDir)..\.."
cmd /c copy-server.bat
EXIT /B 0
Do the same for the
hl_cldll project, except change the batch file to
copy-client
:
cd /D "$(ProjectDir)..\.."
cmd /c copy-client.bat
EXIT /B 0
These commands will simply run the corresponding batch files in the root folder of your repository. Next, we'll create those files.
Go to the root folder of your repository and create the
copy-server.bat file. In this example, that file will be located at
D:\Github\halflife-updated\copy-server.bat. The contents of that file should be a command to copy the
hl.dll file to your mod's location. Here's an example, but you must modify it to match the location of your mod, or it won't work properly!
robocopy ".\projects\vs2019\Debug\hldll" "C:\Program Files (x86)\Steam\SteamApps\common\Half-Life\testmod\dlls" hl.dll /njh /njs /ndl /nc /ns /np
Do the same thing for the
copy-client.bat file. Here's an example of the contents:
robocopy ".\projects\vs2019\Debug\hl_cdll" "C:\Program Files (x86)\Steam\SteamApps\common\Half-Life\testmod\cl_dlls" client.dll /njh /njs /ndl /nc /ns /np
If your project has already been compiled, you should run these batch files now to make sure they copy the files into the right location. Take note that the
Debug folder is hard-coded in these files. For the most part, that's fine - but when you release the final version of your mod, you should consider doing a
Release build, which will put the compiled DLLs into a different spot. You cannot debug a Release build, so only change the build configuration once you're satisfied that everything is working.
Remember: You must update the batch files so that it matches the actual location of your mod! Do not just copy this text without modifying it.
Set up the debugger commands
First, choose a project to use as your "startup project". It doesn't matter which one you choose - so I'll assume you'll be using the
hldll project. Right click on that project, and choose
Set as Startup Project. Then, right click it again and choose
Properties. Select the
Debugging section.
The important options here are
Command and
Command Arguments, and you should also set
Working Directory as well. Here's what you should set them to:
- Command - the full path to your
Steam\SteamApps\common\Half-Life\hl.exe
file, without any quotes around it.- Example:
C:\Program Files (x86)\Steam\SteamApps\common\Half-Life\hl.exe
- Command Arguments - you need a number of flags in here, most of them are pretty important. Be sure to change the text following the
-game
flag to the name of your mod folder.- Example:
-steam -dev -console -window -width 1280 -height 720 -game testmod
- An explanation of each flag:
-steam
- tells the game to launch with Steam-dev -console
- the standard developer commands (when launching HL in this way, the launch properties set up in Steam will not be used)-window -width 1280 -height 720
- force the game to run in windowed mode (you can change the resolution if you like, but windowed mode is important - if you don't run in windowed mode, the game will freeze when you hit a breakpoint, and you will not be able to alt-tab back to VS to continue)-game testmod
- tell the game to launch your mod. Replace testmod
with the name of your mod folder- Another useful flag you might want to add is
+map testmap
, which will automatically load a map called testmap.bsp
at launch
- Working Directory - the full path to the folder that the hl.exe file is located in.
- Example:
C:\Program Files (x86)\Steam\SteamApps\common\Half-Life
Here's what it should look like:
Run the project
You should be all done now - click the green "play" button in VS, or press
F5
to run the project. Assuming everything is set up properly, the code will be compiled, the post-build event will copy the DLLs into your mod, and then VS will launch Half-Life and start debugging it. Try placing a breakpoint on
void CWorld :: Spawn( void )
or
void CHud :: Init( void )
to make sure debugging is working on both the server and client projects respectively.
Debugging on Linux using GDB
Note: in the examples below, replace
<username>
with your Linux username.
If you like, you can use a shell script or update the makefile in order to copy the client and server .so files to your mod directory - but this isn't mandatory since on Linux, the compiler and the debugger processes are entirely separated. This guide assumes that you've already compiled your project and copied the .so files into the correct places.
Setting up a terminal
Open a terminal and change directory to the Half-Life game installation directory. This is typically located under your user directory with this path:
home/<username>.steam/steam/steamapps/common/Half-Life
. If you want to browse to this directory using a Files window or similar interface you may need to enable the
Show hidden files option first to see the
.steam
directory.
Setting the library paths
Open a terminal and enter the following:
export LD_LIBRARY_PATH=/home/<username>/.steam/steam/steamapps/common/Half-Life:/home/<username>/.steam/bin32/steam-runtime/usr/lib/i386-linux-gnu:/home/<username>/.steam/bin32/steam-runtime/lib/i386-linux-gnu
This will set the library paths to search to the directory containing the game executable and the Steam runtime directories containing libraries used by Half-Life 1.
Launching the game
Now enter the following commands in your terminal:
gdb
file hl_linux
set args -dev -console -w 1280 -h 720 -game testmod
run
See the Windows/VS section for an explanation of the launch arguments - you can change them as needed. This will launch Half-Life with the specified mod - be sure to change it to the actual name of your mod folder.
Debugging "could not load library client" errors
Sometimes when developing a mod you will encounter "could not load library client" errors during startup, preventing you from testing or debugging your mod at all.
When this happens it typically means your mod's code references dependencies that can not be found, such as missing libraries or undefined variables and functions. The Half-Life engine does not report the error string for this kind of errors so it is difficult to diagnose.
To help find out what's happening, follow these steps:
On Windows
1. Download this tool:
https://github.com/Solokiller/NativeDllTester/releases. This is a small tool to get the error code returned by
LoadLibrary
when it fails to load a library.
2. Place the executable in the Half-Life directory. This ensures that it will resolve paths the same way that the engine does.
3. Launch the tool. Enter the path to the dll or click Browse to select it, then click Load.
4. If an error occurs, the Error Code field contains the native error code. The translated error message is displayed in the field below that.
5. See the
List of Error Codes to find the corresponding name and description in English. This should help you to pinpoint the cause of the problem that's causing it to fail to load.
On Linux
1. Start by setting up GDB by following the instructions above. If you try to launch your mod in GDB, you will notice that it fails to load there as well.
2. To figure out what's causing the problem, instead of running your mod, enter these commands after the
file hl_linux
command:
set args -dev -console -w 1280 -h 720 -game valve
run
Note the
-game valve
argument - this will launch Half-Life itself, using the unmodified default binaries. We need this because we're going to need an active process instance to execute some code through GDB.
3. Switch to the terminal with your GDB instance and press the button combination CTRL+C. This will interrupt execution and allow us to execute code in GDB directly. Now enter these commands (be sure to replace
<mod directory name>
with the name of your mod directory):
call (void*) dlopen("<mod directory name>/cl_dlls/client.so", 2)
call (char*) dlerror()
The first command calls the function
dlopen
with the path to the mod client library. The second parameter is the constant
RTLD_NOW
which tells
dlopen
to load the library and resolve all undefined symbols. By using this parameter any symbols that cannot be found in any other libraries will cause the library to fail to load, and the missing symbol will be returned as part of the error string.
The second command calls the
dlerror
function. This function returns the error that occurred during the last
dlopen
call, if any errors occurred. If your library fails to load this will be a string explaining what went wrong.
For example, if a global variable is declared but not defined you will get an error string that looks like this:
$2 = 0x8ddf5b0 "<mod directory name>/cl_dlls/client.so: undefined symbol: <variable name>"
With this information you should be able to track down and fix the cause of the problem.
Note: the (void*)
and (char*)
used before the function name are needed because GDB needs to know the return type of the functions in order to print the results. These are C-style casts. If you omit them GDB will not be able to execute the functions and reports an error instead.
For more information about the
dlopen
and
dlerror
functions, consult the
documentation.
Your path is no longer correct because Valve seems to have changed it recently.