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.
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
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
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.
Set a startup project
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
. Next, we want to make sure that both your projects are built when you debug the project, so right click the project again and choose Build Dependencies > Project Dependencies...
. Here, check the box next to hl_cldll
and click OK. Doing this will ensure that when you click the green "Debug" button, both projects will be compiled and copied into your mod directory.
Set up the debugger commands
Right click your startup project again and choose Properties
. Select the Debugging
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.
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.
-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.
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
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 )
void CHud :: Init( void )
to make sure debugging is working on both the server and client projects respectively.
Debugging on Linux using GDB
in the examples below, replace
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:
. 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
Setting the library paths
Open a terminal and enter the following:
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:
set args -dev -console -w 1280 -h 720 -game testmod
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:
1. Download this tool: https://github.com/Solokiller/NativeDllTester/releases
. This is a small tool to get the error code returned by
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.
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
set args -dev -console -w 1280 -h 720 -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
with the path to the mod client library. The second parameter is the constant
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
function. This function returns the error that occurred during the last
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.
For more information about the
functions, consult the documentation