Engine tools








Engine tools are in-game Source tools that are used to aid in development, ease repetitive tasks, and otherwise manipulate the game in real-time.
List of tools
Official tools created by Valve:
- Particle Editor
- Material Editor
- ActBusy Script Editor
- Entity Placement Tool (only in
)
- Commentary Editor
- Foundry
Source Filmmaker
Third-party tools:
Additionally, see the Third Party Engine Tools category.
Usage
To launch a game or mod in tools mode, run a command like this:
hl2.exe -game <mod path> -tools -nop4
Failure to specify -nop4
may lead to the engine exiting silently. This is used to disable Perforce Helix (P4) integration, as most users do not have it installed.
When the game has loaded you will be presented with the first tool in the order as specified in bin/enginetools.txt
(in all games since ) or
bin/sdkenginetools.txt
.
Use F10 to toggle between controlling the game and tool window (not in ), and F11 to toggle visibility of the tool window.
Setting key bindings
Most tools provide a window to set key bindings, which can be opened with Edit -> Key Bindings
. For tools that utilize Qt, use Help -> Keyboard Shortcuts
(only in ).
Each key binding is only active while the tool window is being controlled. All bindings in the "General" and "Global" categories are accessible without being tabbed into an individual window, while other tabs are exclusive to the window they are named after.
Loading new tools

In Left 4 Dead 2 and later, you can manage tool availability with the
toolload
and toolunload
console commands or by editing the engine's bin/enginetools.txt
file. There is a GUI window for this too, but it's currently broken.
In earlier engine branches, tools must be specified in bin/sdkenginetools.txt
in order to load:
"enginetools"
{
"library" "tools/commedit.dll"
"library" "tools/pet.dll"
"library" "tools/vmt.dll"
}
For 64-bit engine builds, use bin/x64/sdkenginetools.txt
. Due to a VPC script bug, 64-bit tool modules are placed in bin/tools/x64
instead of bin/x64/tools
. Therefore, loading 64-bit tools requires directory traversal:
"enginetools"
{
"library" "../tools/x64/commedit.dll"
"library" "../tools/x64/pet.dll"
"library" "../tools/x64/vmt.dll"
}
See the directory containing the tools for your engine branch to know which libraries may be added.
Development
The SDK provides header files in src\public\toolframework
with which new editor tools can be written. Engine tools are usable throughout any mod that depends on the targeted SDK version, making it possible to load custom tools into others' mods.
Tool framework modules must expose an interface derived from IToolDictionary
using VTOOLDICTIONARY_INTERFACE_VERSION
. Tools provided by the dictionary are derived from IToolSystem
.
Creating a custom tool
Follow these steps to create an example tool using the TF2 branch of Source 2013 Multiplayer (64-bit) and Visual Studio 2022. First, create the directory
src\tools\mycustomtool
, and write a VPC script to src\tools\mycustomtool\mycustomtool.vpc
:
$Macro SRCDIR "..\.."
$Macro OUTBINNAME "mycustomtool"
$Macro OUTBINDIR "$SRCDIR\..\game\bin\tools"
$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
$Project "My Custom Tool"
{
$Folder "Source Files"
{
-$File "$SRCDIR\public\tier0\memoverride.cpp"
$File "mycustomtool.cpp"
}
}
This VPC script must be included in src\vpc_scripts\projects.vgc
:
$Project "mycustomtool"
{
"tools\mycustomtool\mycustomtool.vpc" [$WINDOWS]
}
And a group must be defined containing this project in src\vpc_scripts\groups.vgc
:
$Group "mycustomtool"
{
"mycustomtool"
}
The src\tools\mycustomtool\mycustomtool.cpp
source file that was defined in the project VPC script should contain this code:
// Absolute bare minimum example of a custom tool, ideally you should use headers and separate files for each class!!!
#include "toolframework/itoolsystem.h"
#include "toolframework/itooldictionary.h"
class CMyCustomTool : public IToolSystem
{
public:
CMyCustomTool() {}
virtual const char* GetToolName() { return "My Custom Tool"; }
virtual bool Init() { return true; }
virtual void Shutdown() {}
virtual bool ClientInit(CreateInterfaceFn clientFactory) { return true; }
virtual void OnToolActivate() {}
virtual void OnToolDeactivate() {}
virtual bool ServerInit(CreateInterfaceFn serverFactory) { return true; }
virtual void ServerShutdown() {}
virtual void ClientShutdown() {}
virtual bool CanQuit() { return true; }
virtual void PostMessage(HTOOLHANDLE hEntity, KeyValues* message) {}
virtual void Think(bool finalTick) {}
virtual void ServerLevelInitPreEntity() {}
virtual void ServerLevelInitPostEntity() {}
virtual void ServerLevelShutdownPreEntity() {}
virtual void ServerLevelShutdownPostEntity() {}
virtual void ServerFrameUpdatePreEntityThink() {}
virtual void ServerFrameUpdatePostEntityThink() {}
virtual void ServerPreClientUpdate() {}
virtual void ServerPreSetupVisibility() {}
virtual const char* GetEntityData(const char* pActualEntityData) { return pActualEntityData; }
virtual void ClientLevelInitPreEntity() {}
virtual void ClientLevelInitPostEntity() {}
virtual void ClientLevelShutdownPreEntity() {}
virtual void ClientLevelShutdownPostEntity() {}
virtual void ClientPreRender() {}
virtual void ClientPostRender() {}
virtual void AdjustEngineViewport(int& x, int& y, int& width, int& height) {}
virtual bool SetupEngineView(Vector& origin, QAngle& angles, float& fov) { return false; }
virtual bool SetupAudioState(AudioState_t& audioState) { return false; }
virtual bool ShouldGameRenderView() { return true; }
virtual bool IsThirdPersonCamera() { return false; }
virtual bool IsToolRecording() { return false; }
virtual IMaterialProxy* LookupProxy(const char* proxyName) { return nullptr; }
virtual bool TrapKey(ButtonCode_t key, bool down) { return false; }
virtual bool GetSoundSpatialization(int iUserData, int guid, SpatializationInfo_t& info) { return false; }
virtual void RenderFrameBegin() {}
virtual void RenderFrameEnd() {}
virtual void HostRunFrameBegin() {}
virtual void HostRunFrameEnd() {}
virtual void VGui_PreRender(int paintMode) {}
virtual void VGui_PostRender(int paintMode) {}
virtual void VGui_PreSimulate() {}
virtual void VGui_PostSimulate() {}
};
class CToolDictionary : public IToolDictionary
{
public:
// Here is where you should connect new interfaces in the future:
virtual bool Connect(CreateInterfaceFn factory) { return true; }
virtual void Disconnect() {}
virtual void* QueryInterface(const char* szInterfaceName) { return Sys_LoadModule(szInterfaceName, SYS_NOFLAGS); }
virtual InitReturnVal_t Init() { return INIT_OK; }
virtual void Shutdown() { delete m_pMyCustomTool; }
virtual void CreateTools() { m_pMyCustomTool = new CMyCustomTool(); }
virtual int GetToolCount() const { return 1; }
virtual IToolSystem* GetTool(int index) { return m_pMyCustomTool; }
private:
CMyCustomTool* m_pMyCustomTool;
};
static CToolDictionary g_ToolDictionary;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CToolDictionary, IToolDictionary, VTOOLDICTIONARY_INTERFACE_VERSION, g_ToolDictionary);
To create the solution in order to build, add +mycustomtool
to src\createallprojects.bat
:
devtools\bin\vpc.exe /hl2mp /tf /define:SOURCESDK +everything +mycustomtool /mksln everything.sln
Afterwards, run it and open the src\everything.sln
solution with Visual Studio 2022. Once loaded, build the project titled "My Custom Tool".
Building the project will produce game\bin\tools\x64\mycustomtool.dll
, which would need to be copied to Source SDK Base 2013 Multiplayer\bin\tools\x64
and defined in Source SDK Base 2013 Multiplayer\bin\x64\sdkenginetools.txt
:
"enginetools"
{
"library" "../tools/x64/commedit.dll"
"library" "../tools/x64/pet.dll"
"library" "../tools/x64/vmt.dll"
"library" "../tools/x64/mycustomtool.dll"
}
Then, run the SDK with the launch parameters -game <mod path> -tools -nop4
to run with tools enabled. Once in-game, "My Custom Tool" will now be accessible in the Tools
menu from other tools, where activating it will simply just return you to the game without any way to switch back to other tools.
This is because custom engine tools are responsible for implementing their own UI code, and the example provides does not provide any UI. The tool switching behavior may be defined using enginetools->SwitchToTool(int index)
, although doing so would require connecting the enginetools
interface beforehand.