Developer Console Control
This document explains how to write code that prints to the console, as well as how to execute and create console commands and variables. See Developer Console for an overview of console features.
Printing to the console
Printing text to the console is done the same way in all modules, since the Tier0 debug layer provides these routines. The three most common functions are Msg()
, DevMsg()
and Warning()
which all support VarArgs, like sprintf()
:
DevMsg (char const* pMsg, ... ) - only in developer mode
Msg(char const* pMsg, ... ) - always, white
Warning(char const *pMsg, ... ) - always, red
For backward compatibility with HL1 source code you can still use Con_Printf()
and Con_DPrintf()
.
Including variables:
Float:
DevMsg("Include this variable: %.2f", floatVariable)
Integer:
DevMsg("Include this variable: %i", integerVariable)
String:
DevMsg("Include this variable: %s", stringVariable)
Multiple Variable:
DevMsg("Include this %i followed by %s and the %.2f", integerVariable, stringVariable, floatVariable)
Include \n
to create carriage return, ready to output on line below when Msg/DevMsg next called.
Setting cvars
[the_cvar]->SetValue( [value] );
If you do not have access to a cvar, do this first:
ConVar *[the_cvar] = cvar->FindVar( "[the_cvar]" );
This works even for closed-source cvars.
Executing commands
The engine provides interface to server and client code to execute commands (strings) from code as if the user would have entered them. The server has to use the interface IVEngineServer::ServerCommand()
:
engine->ServerCommand("changelevel de_dust\n");
Client code has to use the interface IVEngineClient
and can choose here between two functions, depending if the command should be executed on the client first or be sent directly to the server:
engine->ServerCmd( "say hello\n" ); // send command to server
or
engine->ClientCmd( "say hello\n" ); // execute command on client
If you wish to execute a console command on a client from the server use:
engine->ClientCommand( edict(), "command", ... );
Where edict() is your ent's edict, "command" would be replaced by your concommand, followed by any formatted vars (same as printf). This would be useful for showing a team selection menu.
Adding new commands & variables
The developer console is a subsystem provided by the Source engine and is accessible for all other modules via a public interface ICvar
( see \public\icvar.h
). This interface allows registering new commands and finding/iterating existing commands. This interface is available via the global CVAR in-game server/client code (cv in engine code). Console commands are implemented in ConCommand
and console_variables in ConVar
, which both derive from the base class ConCommandBase
(see \public\convar.h
).
Adding a new command or variable is fairly simple and the same code in both server and client (even engine) modules. The constructor of these classes automatically registers the command at the console system. This short example code adds a new command my_function and a new variable my_variable initialized to 42:
#include <convar.h>
ConVar my_variable( "my_variable", "42", FCVAR_ARCHIVE, "My favorite number" );
void MyFunction_f( void )
{
Msg("This is my function\n");
}
ConCommand my_function( "my_function", MyFunction_f, "Shows a message.", FCVAR_CHEAT );
It is common use that the object name and the command name are the same and variables used only in a single source file are declared as static.
Using the ConVar class
ConVars store variables that can be altered through the console and (optionally) saved in config.cfg. Let's take a look at the most used constructor:
ConVar( char const *pName,
char const *pDefaultValue,
int flags,
char const *pHelpString )
The first argument pName
is the variable name (no spaces), followed by pDefaultValue
, which is always given as a string even for ConVars with numerical values. Flags
specify special characteristics of the variable — all flag definitions start with a FCVAR_
prefix. More information about these flags can be found below. It's always good to provide a pHelpString
, so users get an idea what this variable is about. ConVars are not bound to a certain type, their value can be an integer or a float or string and you may use it however you like. As long as you have the ConVar object itself or as a pointer, you can access and modify its values directly. All these examples are valid and have the same result:
if ( my_variable.GetInt() == 42 ) DoSomething();
if ( my_variable.GetFloat() == 42.0f ) DoSomething();
if ( strcmp(my_variable.GetString(), "42")==0 ) DoSomething();
To set the value of a ConVar you should use the SetValue()
function, which also allows all types of data:
my_variable.SetValue( 42 );
my_variable.SetValue( 42.0f );
my_variable.SetValue( "42" );
At any time you can revert a ConVar back to it's original default value by using the Revert()
function.
If a ConVar is created in a different module, the ICvar
interface function FindVar()
can be used to get a pointer to this object, if the variable name is known. This is an expensive search function and the pointer should be cached if reused often. Here is an example how to check the ConVar sv_cheats defined in the engine module:
ConVar *pCheats = cvar->FindVar( "sv_cheats" );
if ( pCheats && pCheats->GetInt() == 1 ) AllowCheating();
A range of valid values can be specified for numerical ConVars using a different constructor. Then a ConVar is automatically checked by the console system whenever changed manually. If the entered number is out of range, it's rounded to the next valid value. Setting valid range from 1 to 100:
ConVar my_variable( "my_variable", "42", 0, "helptext", true, 1, true, 100 );
Sometimes you also want a notification when a user's or another subsystem changes your ConVar value, therefore a callback function can be installed:
static void OnChangeMyVariable ( IConVar *var, const char *pOldValue, float flOldValue )
{
DevMsg( "ConVar %s was changed from %s to %s\n", var->GetName(), pOldValue, (( ConVar* )var)->GetString() );
}
ConVar my_variable( "my_variable", "42", 0, "My favorite number", OnChangeMyVariable );
Using the ConCommand class
The class
ConCommand does not store a value, but instead executes a procedure as soon as it is invoked. It is simpler than the ConVar and has just one constructor:
ConCommand( char const *pName,
FnCommandCallback callback,
char const *pHelpString = 0,
int flags = 0,
FnCommandCompletionCallback completionFunc = 0 );
As in ConVar, pName
specifies the command name (no spaces). callback
is the function executed when a user runs this command and both pHelpString
and flags
have the same function as in ConVar. ConCommands supports auto completion for the first parameter, which is useful especially for commands that process files. For example, if you have a command loadtext <textfile>
that expects a .txt file as input, the console scans for all available .txt files and allows the user to choose one from a list. If a valid completionFunc
is passed, it will be called whenever the console system needs a list of available arguments.
When the callback
function is executed, the parameters entered in the console are not passed as function arguments. The callback
function has to query the engine how many arguments where given using the engine interface function args.ArgC()
, where 'args' is the name of the CCommand in your function head. Then you can look at single arguments using args.Arg(index)
, where index 1 is the first argument. The arguments are always returned as strings.
void MySay_f ( const CCommand &args )
{
if ( args.ArgC() < 1 || args.Arg(1) == "" )
{
Msg("Usage: my_say <text>\n");
return;
}
Msg("I say: %s\n", args.Arg(1) );
}
ConCommand my_say( "my_say", MySay_f , "say something", 0);
Here an example how to build a simple auto complete list. The partial parameter isn't used here; it contains the characters entered so far (including the command name itself) :
static int MySayAutoComplete ( char const *partial,
char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] )
{
strcpy( commands[0], "hello" );
strcpy( commands[1], "goodbye" );
return 2; // number of entries
}
ConCommand my_say( "my_say", MySay_f, "say something", 0, MySayAutoComplete);
To retrieve the calling player from a concommand, use UTIL_GetCommandClient()
on the server or C_BasePlayer::GetLocalPlayer()
on the client.
Player Client Commands
All server-side player classes derived from CBasePlayer have a function, ClientCommand, which is called when the player issues a ConCommand. This function can be used to handle concommands.
bool CHL2MP_Player::ClientCommand( const char *pcmd )
{
if ( FStrEq( pcmd, "spectate" ) )
{
if ( ShouldRunRateLimitedCommand( pcmd ) )
{
// instantly join spectators
HandleCommand_JoinTeam( TEAM_SPECTATOR );
}
return true;
}
else if ( FStrEq( pcmd, "jointeam" ) )
{
if ( engine->Cmd_Argc() < 2 )
{
Warning( "Player sent bad jointeam syntax\n" );
}
if ( ShouldRunRateLimitedCommand( pcmd ) )
{
int iTeam = atoi( engine->Cmd_Argv(1) );
HandleCommand_JoinTeam( iTeam );
}
return true;
}
else if ( FStrEq( pcmd, "joingame" ) )
{
return true;
}
return BaseClass::ClientCommand( pcmd );
}
As you can see in this example HL2MP code, the function is called with an argument, pcmd, which is then handled based on several if statements. The function ShouldRunRateLimitedCommand is used to prevent spamming and checks to make sure that the command wasn't too recently called.
This function will look slightly different in code derived from the Orangebox engine.
The FCVAR flags
The console command/variable flags can specify quite powerful characteristics and must be handled with care. These flags are usually set in the constructor but may be modified with ConCommandBase::AddFlags()
(not used very often). Users do not typically have the ability to modify these flags.
FCVAR_CHEAT
- In multiplayer, prevents this command/variable from being used unless the server has
sv_cheats
turned on. If a client connects to a server where cheats are disabled (which is the default), all client side console variables labeled asFCVAR_CHEAT
are reverted to their default values and can't be changed as long as the client stays connected. Console commands marked asFCVAR_CHEAT
can't be executed either. - As a general rule of thumb, any client-side command that isn't specifically meant to be configured by users should be marked with this flag, as even the most harmless looking commands can sometimes be misused to cheat. For server-side only commands you can be more lenient, since these would have no effect when changed by connected clients anyway.
- Note:Has no effect in singleplayer. If you want a command to be locked behind
sv_cheats
in singleplayer (e.g. so using it voids achievements), you must add code to the command which explicitly checks if cheats are enabled when attempting to use/change it. FCVAR_DEVELOPMENTONLY
- Hides this command from auto complete,
cvarlist
, etc. and prevents it from being run; the engine will effectively act like it doesn't exist. This flag is removed automatically if tier1 was compiled in debug mode (possible in SDK 2013, but not Alien Swarm) or can be removed manually usingConCommandBase::RemoveFlags()
in game code. - Warning:Despite code comments implying as such, commands with this flag are not compiled out! Server plugins (and VScript in Team Fortress 2) can change the values of dev-only console variables and/or remove the flag completely. If you have a command that you want to be absolutely sure is never run in release builds, you must wrap it in an
#ifdef _DEBUG
. FCVAR_HIDDEN
- Hides this command from auto complete,
cvarlist
, etc. likeFCVAR_DEVELOPMENTONLY
, but still allows it to be run through config files or if manually typed by the user. Used for things which the user should have access to, but generally shouldn't be changing manually. FCVAR_SERVER_CAN_EXECUTE
- Allows a client-side command to be run by the server via
IVEngineServer::ClientCommand()
. If the server attempts to run a command without this flag, the client's console will print an error message and it will be ignored. In singleplayer, this is disabled and all commands can be executed by the server. - Note:In mods, this restriction is disabled by default; you must call
IVEngineClient::SetRestrictServerCommands()
to enable it. FCVAR_CLIENTCMD_CAN_EXECUTE
- Allows this command to be executed by
IVEngineClient::ClientCmd()
. There is alsoIVEngineClient::ClientCmd_Unrestricted()
, which ignores this flag and will execute any command. - Todo: What actually is the purpose of this, why would clients need to restrict commands that didn't come from the server? Which Valve games have this restriction enabled?
- Note:In mods, this restriction is disabled by default; you must call
IVEngineClient::SetRestrictClientCommands()
to enable it. FCVAR_NOT_CONNECTED
- Prevents this variable from being changed while the client is currently in a server, due to the possibility of exploitation of the command (e.g. fps_max).
- Confirm:Does this have any effect in singleplayer? It might vary between engine branches
FCVAR_USERINFO
- When used on a client-side console variable, its value is transmitted to the server and updated every time the user changes it. This should be used for client information the server needs to know about, like the player's name or their network settings. When the player changes one of these variables the engine notifies the server code via
ClientSettingsChanged()
. The game server can also query the engine for specific client settings withGetClientConVarValue()
. FCVAR_REPLICATED
- When set on a console variable, all connected clients will be forced to match the server-side value. This should be used for shared code where it's important that both sides run the exact same path using the same data (e.g. predicted movement/weapons, game rules).
FCVAR_ARCHIVE
- When set on a console variable, its value is saved in the file
config.cfg
when the game shuts down or the user manually runshost_writeconfig
. This file is executed when the game is reopened, restoring the previously set values. This should be used for any settings that are exposed to the user through UI or otherwise expected to persist across game restarts (likename
ornetwork_rate
). - Tip:Avoid combining this with flags that restrict the command's usage or change its value, such as
FCVAR_CHEAT
orFCVAR_REPLICATED
. This can cause settings exposed in the UI to get unexpectedly reset or changed. FCVAR_ARCHIVE_XBOX
- Like
FCVAR_ARCHIVE
, but for Xbox 360. Needless to say, this is not particularly useful to most modders. FCVAR_NOTIFY
- If set on a console variable, the server prints a notification message to all clients' chat whenever the variable is changed. This should be used for variables that change game play rules, which are important for players to know about (
mp_friendlyfire
etc). FCVAR_PROTECTED
- Marks a console variable as "confidential", hiding it from several places where it would normally be visible. The value will never be networked to clients, and if it is also flagged with
FCVAR_NOTIFY
, the notification message will replace the value with the string***PROTECTED***
. This should be used for data such as passwords which clients shouldn't know about. FCVAR_SERVER_CANNOT_QUERY
- Prevents the server from being able to query the value of a client-side console variable. Effectively the inverse of
FCVAR_PROTECTED
, and should be used in similar cases. FCVAR_SPONLY
- Prevents the command from being used in multiplayer.
FCVAR_PRINTABLEONLY
- Restricts a console variable's value to only contain only printable characters (no control chars etc). This should be used for variables that are logged or broadcasted (gamerules etc), to prevent arbitrary code execution and other problems.
FCVAR_NEVER_AS_STRING
- Tells the engine never to print this variable as a string since it contains control sequences.
FCVAR_DEMO
- When starting to record a demo file, explicitly adds the value of this console variable to the recording to ensure a correct playback.
FCVAR_DONTRECORD
- Prevents this command from being recorded in demo files; effectively the opposite of
FCVAR_DEMO
. - Warning:Doesn't prevent the command from being run if a demo file is modified to include it anyway. Make sure to add an explicit
engine->IsPlayingDemo()
check to the command's code if this could potentially be used maliciously. FCVAR_RELOAD_MATERIALS
- If set and this variable changes, it forces a material reload.
FCVAR_RELOAD_TEXTURES
- If set and this variable changes, it forces a texture reload.
FCVAR_SS
(in all games since )- Automatically generates copies of this console variable corresponding to each splitscreen player. The additional copies are numbered from 2 to
MAX_SPLITSCREEN_PLAYERS
, with the original un-numbered copy being used for player 1. Thus, ifMAX_SPLITSCREEN_PLAYERS
is set to 1 then this will have no effect; you do not need to go out of your way to remove the flag in mods which don't have splitscreen. FCVAR_SS_ADDED
(in all games since )- Internal flag automatically added to console variables created by
FCVAR_SS
. Shouldn't be set manually. FCVAR_RELEASE
(in all games since )- In the Left 4 Dead series, all commands that don't have this flag are marked as
FCVAR_DEVELOPMENTONLY
automatically. Enabling this behavior requires recompiling the engine, making this flag useless to modders unless one were to reimplement the functionality in game code.
The following additional flags are set automatically by the console system depending on which module the command is defined in:
- FCVAR_LAUNCHER
- FCVAR_GAMEDLL
- FCVAR_CLIENTDLL
- FCVAR_MATERIAL_SYSTEM
- FCVAR_STUDIORENDER
These are mainly for internal engine use and shouldn't be set manually.