Developer Console Control: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
m (linkification)
(→‎The FCVAR flags: reduce this to a smaller list of just "important" flags now that there is a dedicated cvar flags page)
 
(63 intermediate revisions by 34 users not shown)
Line 1: Line 1:
[[Category:Programming]]
{{LanguageBar}}
[[Image:developer_console_01.gif]]
 
 


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.
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 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 3 most common functions are <code>Msg()</code>, <code>DevMsg()</code> and <code>Warning()</code> which all support formatted string output like <code>sprintf()a</code>:
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 <code>Msg()</code>, <code>DevMsg()</code> and <code>Warning()</code> which all support VarArgs, like <code>sprintf()</code>:


<pre>
<source lang="cpp">
DevMsg (char const* pMsg, ... ) - only in developer mode
DevMsg (char const* pMsg, ... ) - only in developer mode
Msg(char const* pMsg, ... ) - always, white
Msg(char const* pMsg, ... ) - always, white
Warning(char const *pMsg, ... ) - always, red
Warning(char const *pMsg, ... ) - always, red
</pre>
</source>
 
{{note|Only 989 characters can be output to the console at once!}}


For backward compatibility with HL1 source code you can still use <code>Con_Printf()</code> and <code>Con_DPrintf()</code>.
For backward compatibility with HL1 source code you can still use <code>Con_Printf()</code> and <code>Con_DPrintf()</code>.


=Executing commands=
'''Including variables:'''
 
Float:
<source lang="cpp">
DevMsg("Include this variable: %.2f", floatVariable)
</source>
 
Integer:
<source lang="cpp">DevMsg("Include this variable: %i", integerVariable)</source>
 
String:
<source lang="cpp">DevMsg("Include this variable: %s", stringVariable)</source>
 
Multiple Variable:
<source lang="cpp">
DevMsg("Include this %i followed by %s and the %.2f", integerVariable, stringVariable, floatVariable)
</source>
 
Include <code>\n</code> to create carriage return, ready to output on line below when Msg/DevMsg next called.
 
{{Note| There exist versions of these functions which have an additional parameter called '''level'''. If '''level''' is higher than the developer cvar setting, the message will not be printed in the console. This allows you to print extended debug info only when developer mode is at a certain setting. See [[Developer]] for more info on which levels print what kind of information.}}
 
== Setting cvars ==
 
<source lang="cpp">[the_cvar]->SetValue( [value] );</source>
 
If you do not have access to a cvar, do this first:
<source lang="cpp">
ConVar *[the_cvar] = cvar->FindVar( "[the_cvar]" );
</source>
 
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 <code>IVEngineServer::ServerCommand()</code>:
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 <code>IVEngineServer::ServerCommand()</code>:
<source lang="cpp">
engine->ServerCommand("changelevel de_dust\n");
</source>
Client code has to use the interface <code>IVEngineClient</code> 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:
<source lang="cpp">
engine->ServerCmd( "say hello\n" ); // send command to server
</source>


<pre>
or
engine->ServerCommand("changelevel de_dust");
</pre>


Client code has to use the interface <code>IVEngineClient</code> 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:
<source lang="cpp">
engine->ClientCmd( "say hello\n" ); // execute command on client
</source>
 
If you wish to execute a console command on a client from the server use:


<pre>
<source lang="cpp">engine->ClientCommand( edict(), "command", ... );</source>
engine->ServerCmd( "say hello" ); // send command to server
</pre>


or
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.


<pre>
{{note|You can't pass dynamic arguments to these functions. All parameters are decided when the code compiles.}}
engine->ClientCmd( "say hello" ); // execute command on client
</pre>


=Adding New Commands & Variables=
== 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 <code>ICvar</code> ( see <code>\public\icvar.h</code>). 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 <code>ConCommand</code> and console variables in <code>ConVar</code>, which both derive from the base class <code>ConCommandBase</code> (see <code>\public\convar.h</code>).
The developer console is a subsystem provided by the Source engine and is accessible for all other modules via a public interface <code>ICvar</code> ( see <code>\public\icvar.h</code>). 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 command]]s are implemented in <code>ConCommand</code> and [[Console Variable|console_variables]] in <code>ConVar</code>, which both derive from the base class <code>ConCommandBase</code> (see <code>\public\convar.h</code>).


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:
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:
 
<source lang="cpp">
<pre>
#include <convar.h>
#include <convar.h>
ConVar my_variable( "my_variable", "42", FCVAR_ARCHIVE, "My favorite number" );
ConVar my_variable( "my_variable", "42", FCVAR_ARCHIVE, "My favorite number" );
Line 52: Line 93:


ConCommand my_function( "my_function", MyFunction_f, "Shows a message.", FCVAR_CHEAT );
ConCommand my_function( "my_function", MyFunction_f, "Shows a message.", FCVAR_CHEAT );
</pre>
</source>


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.
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 class ConVar=
== Using the ConVar class ==


First let's take a look at the most used ConVar constructor:
[[ConVar]]s 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:


<pre>
<source lang="cpp">
ConVar( char const *pName,
ConVar( char const *pName,
  char const *pDefaultValue,
char const *pDefaultValue,
  int flags,
int flags,
  char const *pHelpString )
char const *pHelpString )
</pre>
</source>


The first argument <code>pName</code> is the variable name (no spaces), followed by <code>pDefaultValue</code>, which is always given as a string even for ConVars with numerical values. <code>Flags</code> specify special characteristics of the variable, all flag definitions start with <code>FCVAR_*</code>, more about these flags later. It's always good to provide a <code>pHelpString</code>, 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:
The first argument <code>pName</code> is the variable name (no spaces), followed by <code>pDefaultValue</code>, which is always given as a string even for ConVars with numerical values. <code>Flags</code> specify special characteristics of the variable &mdash; all flag definitions start with a <code>FCVAR_</code> prefix. More information about these flags can be found [[#The FCVAR flags|below]]. It's always good to provide a <code>pHelpString</code>, 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:
 
<source lang="cpp">
<pre>
if ( my_variable.GetInt() == 42 ) DoSomething();
if ( my_variable.GetInt() == 42 ) DoSomething();
if ( my_variable.GetFloat() == 42.0f ) DoSomething();
if ( my_variable.GetFloat() == 42.0f ) DoSomething();
if ( strcmp(my_variable.GetString(), "42")==0 ) DoSomething();
if ( strcmp(my_variable.GetString(), "42")==0 ) DoSomething();
</pre>
</source>
 
To set the value of a <code>ConVar</code> you should use the <code>SetValue()</code> function, which also allows all types of data:


<pre>
To set the value of a ConVar you should use the <code>SetValue()</code> function, which also allows all types of data:
<source lang="cpp">
my_variable.SetValue( 42 );
my_variable.SetValue( 42 );
my_variable.SetValue( 42.0f );
my_variable.SetValue( 42.0f );
my_variable.SetValue( "42" );
my_variable.SetValue( "42" );
</pre>
</source>
 
At any time you can revert a <code>ConVar</code> back to it's original default value by using the <code>Revert()</code> function.


If a <code>ConVar</code> is created in a different module, the <code>ICvar</code> interface function <code>FindVar()</code> 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 <code>sv_cheats</code> defined in the engine module:
At any time you can revert a ConVar back to it's original default value by using the <code>Revert()</code> function.


<pre>
If a ConVar is created in a different module, the <code>ICvar</code> interface function <code>FindVar()</code> 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:
<source lang="cpp">
ConVar *pCheats  = cvar->FindVar( "sv_cheats" );
ConVar *pCheats  = cvar->FindVar( "sv_cheats" );


    if ( pCheats && pCheats->GetInt() == 1 ) AllowCheating();
if ( pCheats && pCheats->GetInt() == 1 ) AllowCheating();
</pre>
</source>


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:
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:
 
<source lang="cpp">
<pre>
ConVar my_variable( "my_variable", "42", 0, "helptext", true, 1, true, 100 );
ConVar my_variable( "my_variable", "42", 0, "helptext", true, 1, true, 100 );
</pre>
</source>


Sometimes you also want a notification when a users or another subsystem changes your ConVar value, therefore a callback function can be installed:
Sometimes you also want a notification when a user's or another subsystem changes your ConVar value, therefore a callback function can be installed:
 
<source lang="cpp">
<pre>
static void OnChangeMyVariable ( IConVar *var, const char *pOldValue, float flOldValue )
static void OnChangeMyVariable ( ConVar *var, char const *pOldString )
{
{
    DevMsg( "ConVar %s was changed from %s to %s\n", var->GetName(), pOldString, var->GetString() );
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 );
ConVar my_variable( "my_variable", "42", 0, "My favorite number", OnChangeMyVariable );
</pre>
</source>


=Using the class ConCommand=
== Using the ConCommand class ==


The <code>class</code> ConCommand is simpler than the ConVar and has just one constructor:
The <code>class</code> 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:
 
<source lang="cpp">
<pre>
ConCommand( char const *pName,
ConCommand( char const *pName,
    FnCommandCallback callback,
FnCommandCallback callback,
    char const *pHelpString = 0,
char const *pHelpString = 0,
    int flags = 0,
int flags = 0,
    FnCommandCompletionCallback completionFunc = 0 );
FnCommandCompletionCallback completionFunc = 0 );
</pre>
</source>


As in ConVar <code>pName</code> specifies the command name (no spaces!). <code>callback</code> is the function executed when a user runs this command, <code>pHelpString</code> and flags have the same function as in ConVar. ConCommands supports auto completion for the first parameter, which is usefully especially for commands that process files. For example, if you have a command <code>loadtext lt;textfilegt</code>; 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 <code>completionFunc</code> is passed, it will be called whenever the console system needs a list of available arguments.
As in ConVar, <code>pName</code> specifies the command name (no spaces). <code>callback</code> is the function executed when a user runs this command and both <code>pHelpString</code> and <code>flags</code> 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 <code>loadtext &lt;textfile&gt;</code> 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 <code>completionFunc</code> is passed, it will be called whenever the console system needs a list of available arguments.


When the <code>callback</code> function is executed, the parameters entered in console are not passed as function arguments. The <code>callback</code> function has to query the engine how many arguments where given using the engine interface function <code>Cmd_Argc()</code>. Then you can look at single arguments using <code>Cmd_Argv(index)</code>, where index 1 is the first argument. The arguments are always returned as strings.
When the <code>callback</code> function is executed, the parameters entered in the console are not passed as function arguments. The <code>callback</code> function has to query the engine how many arguments where given using the engine interface function <code>args.ArgC()</code>, where 'args' is the name of the [[CCommand]] in your function head. Then you can look at single arguments using <code>args.Arg(index)</code>, where index 1 is the first argument. The arguments are always returned as strings.
<source lang="cpp">
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);
</source>


<pre>
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) :
void MySay_f ( void )
<source lang="cpp">
static int MySayAutoComplete ( char const *partial,
char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] )
{
{
    if ( engine->Cmd_Argc() < 1 )
strcpy( commands[0], "hello" );
    {
strcpy( commands[1], "goodbye" );
        Msg(""Usage: my_say <text>\n");
return 2; // number of entries
        return;
}
    }


    Msg("I say: %s\n", engine->Cmd_Argv(1) );
ConCommand my_say( "my_say", MySay_f, "say something", 0, MySayAutoComplete);
}
</source>


ConCommand my_say( "my_say", MySay_f, "say something", 0);
To retrieve the calling player from a concommand, use <code>UTIL_GetCommandClient()</code> on the server or <code>C_BasePlayer::GetLocalPlayer()</code> on the client.
</pre>


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 (includeing the command name itself) :
== Player Client Commands ==


<pre>
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.
static int MySayAutoComplete ( char const *partial,
<source lang="cpp">
char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] )
bool CHL2MP_Player::ClientCommand( const char *pcmd )
{
{
    strcpy( commands[0], "hello" );
if ( FStrEq( pcmd, "spectate" ) )
    strcpy( commands[1], "goodbye" );
{
    return 2; // number of entries
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 );
}
}
</source>


ConCommand my_say( "my_say", MySay_f, "say something", 0, MySayAutoComplete);
</pre>


=The FCVAR_ flags=
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 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 <code>ConCommandBase::AddFlags()</code> (not used very often). It's not possible to change these flags other than in source code to avoid cheating. Some flags must be set manually, others are set automatically by then console system:
== The FCVAR flags ==


<code>FCVAR_LAUNCHER, FCVAR_GAMEDLL, FCVAR_CLIENTDLL, FCVAR_MATERIAL_SYSTEM, FCVAR_STUDIORENDER</code>
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 <code>ConCommandBase::AddFlags()</code> (not used very often). Users do not have the ability to modify these flags without the use of plugins or other custom code.


These flags are set by the registration process and specify the module, where the command was created (you don't need to set them). The following flags must be set manually:
There are [[Console command flags|many different flags]] supported by the engine, but you probably won't need to use most of them outside of specific edge-cases. Here are some of the most common or important ones:


{|
;<code>FCVAR_CHEAT</code>
| <code>FCVAR_CHEAT</code>
: In multiplayer, prevents this command/variable from being used unless the server has <code>sv_cheats</code> turned on. If a client connects to a server where cheats are disabled (which is the default), all client side console variables labeled as <code>FCVAR_CHEAT</code> are reverted to their default values and can't be changed as long as the client stays connected. Console commands marked as <code>FCVAR_CHEAT</code> 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.
| Most commands and variables are for debugging proposes and not removed in release builds since they are useful 3rd party developers and map makers too. Unfortunately we cannot allow normal players to use these debugging tools since it's an unfair advantage over other players (cheating). A good rule is to add <code>FCVAR_CHEAT</code> basically to every new console command you add unless it's an explicit and legitimate options setting for players. Experience shows that even the most harmless looking debugging command can be misused as a cheat somehow.
:{{note|Has no effect in singleplayer. If you want a command to be locked behind <code>sv_cheats</code> 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.}}
;<code>FCVAR_ARCHIVE</code>
: When set on a console variable, its value is saved in the file <code>config.cfg</code> when the game shuts down or the user manually runs <code>host_writeconfig</code>. 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 (like <code>name</code> or <code>network_rate</code>).
:{{tip|Avoid combining this with flags that restrict the command's usage or change its value, such as <code>FCVAR_CHEAT</code> or <code>FCVAR_REPLICATED</code>. This can cause settings exposed in the UI to get unexpectedly reset or changed.}}
;<code>FCVAR_REPLICATED</code>
: 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).
;<code>FCVAR_SERVER_CAN_EXECUTE</code>
: Allows a client-side command to be run by the server via <code>IVEngineServer::ClientCommand()</code>. 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 <code>IVEngineClient::SetRestrictServerCommands()</code> to enable it.}}
;<code>FCVAR_USERINFO</code>
: 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 <code>ClientSettingsChanged()</code>. The game server can also query the engine for specific client settings with <code>GetClientConVarValue()</code>.
;<code>FCVAR_NOTIFY</code>
: 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 (<code>mp_friendlyfire</code> etc).
;<code>FCVAR_SS</code> {{l4d|since}}
: Automatically generates copies of this console variable corresponding to each splitscreen player. The additional copies are numbered from 2 to <code>MAX_SPLITSCREEN_PLAYERS</code>, with the original un-numbered copy being used for player 1. Thus, if <code>MAX_SPLITSCREEN_PLAYERS</code> 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.


The game server's setting of <code>sv_cheats</code> decides if cheats are enabled or not. If a client connects to a server where cheats are disabled (should be the default case), all client side console variables labeled as <code>FCVAR_CHEAT</code> are reverted to their default values and can't be changed as long as the client stays connected. Console commands marked as <code>FCVAR_CHEAT</code> can't be executed either.
[[Category:Programming]]
|-
[[Category:Console]]
| <code>FCVAR_USERINFO</code>
|-
| Some console variables contain client information the server needs to know about, like the player's name or his network settings. These variables must be flagged as <code>FCVAR_USERINFO</code>, so they get transmitted to the server and updated every time the user changes them. When the player changes on of these variables the engine notifies the server code via <code>ClientSettingsChanged()</code>. Then the game server can query the engine for specific client settings with <code>GetClientConVarValue()</code>.
|-
| <code>FCVAR_REPLICATED</code>
|-
| Game server and client are using shared code where it's important that both sides run the exact some path using the same data (e.g. predicted movement/weapons, game rules). If this code uses console variables, they must match the same values on both sides. The flag <code>FCVAR_REPLICATED</code> ensures that by broadcasting these values to all clients. While connected, clients can't change these values and are force to use the server side values.
|-
| <code>FCVAR_ARCHIVE</code>
|-
| Some console variables contain user specific settings we want to restore each time the game is started ( like name or <code>network_rate</code>). If a console variable is labeled as <code>FCVAR_ARCHIVE</code>, it is saved in the file <code>config.cfg</code> when the game shuts down and is reloaded with the next start. (Also the command <code>host_writeconfig</code> stores all <code>FCVAR_ARCHIVE</code> variables to a file).
|-
| <code>FCVAR_NOTIFY</code>
|-
| If a console variable is flagged as <code>FCVAR_NOTIFY</code>, a server sends a notification message to all clients whenever this variable is changed. This should be used for variables that change game play rules, which are important for all players (<code>mp_friendlyfire</code> etc).
|-
| <code>FCVAR_PROTECTED</code>
|-
| If console variables contain private information (passwords etc), we don't want them to be visible to other players. Then the <code>FCVAR_PROTECTED</code> flag should be set to label this information as confidential.
|-
| <code>FCVAR_SPONLY</code>
|-
| Sometimes executing commands or changing variables may be valid only in single player mode, then label these commands as <code>FCVAR_SPONLY</code>.
|-
| <code>FCVAR_PRINTABLEONLY</code>
|-
| Some important variables are logged or broadcasted (gamerules etc) and it is important that they contain only printable characters (no control chars etc).
|-
| <code>FCVAR_NEVER_AS_STRING</code>
|-
| The flag tells the engine never to print this variable as string since it contains control sequences.
|-
| <code>FCVAR_DEMO</code>
|-
| When starting recording a demo file, some console variables must explicitly added to the recording to ensure a correct playback.
|-
| <code>FCVAR_DONTRECORD</code>
|-
| This is the opposite of FCVAR_DEMO, some console commands shouldn't be recorded in demo files.
|}

Latest revision as of 21:35, 1 August 2025

English (en)Deutsch (de)Русский (ru)中文 (zh)Translate (Translate)


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
Note.pngNote:Only 989 characters can be output to the console at once!

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.

Note.pngNote: There exist versions of these functions which have an additional parameter called level. If level is higher than the developer cvar setting, the message will not be printed in the console. This allows you to print extended debug info only when developer mode is at a certain setting. See Developer for more info on which levels print what kind of information.

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.

Note.pngNote:You can't pass dynamic arguments to these functions. All parameters are decided when the code compiles.

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 have the ability to modify these flags without the use of plugins or other custom code.

There are many different flags supported by the engine, but you probably won't need to use most of them outside of specific edge-cases. Here are some of the most common or important ones:

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 as FCVAR_CHEAT are reverted to their default values and can't be changed as long as the client stays connected. Console commands marked as FCVAR_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.pngNote: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_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 runs host_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 (like name or network_rate).
Tip.pngTip:Avoid combining this with flags that restrict the command's usage or change its value, such as FCVAR_CHEAT or FCVAR_REPLICATED. This can cause settings exposed in the UI to get unexpectedly reset or changed.
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_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.pngNote:In mods, this restriction is disabled by default; you must call IVEngineClient::SetRestrictServerCommands() to enable it.
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 with GetClientConVarValue().
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_SS (in all games since Left 4 Dead)
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, if MAX_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.