Developer Console Control
Dieser Artikel beschreibt, wie Code geschrieben werden kann, der auf der Konsole ausgegeben wird, genauso wie Konsolenbefehle und -Variablen angelegt werden können. Siehe Developer Console Für eine Übersicht an Konsolenfeatures.
Auf der Konsole ausgeben
Text auf der Konsole auszugeben wird in allen Modulen auf die gleiche weise erledigt, da die Tier0 -Debugebene diese Routinen liefert. Die 3 üblichen Funktionen sind Msg()
, DevMsg()
und Warning()
, welche alle VarArgs unterstützen, wie sprintf()
:
DevMsg (char const* pMsg, ... ) - nur im Entwicklermodus
Msg(char const* pMsg, ... ) - immer, white
Warning(char const *pMsg, ... ) - immer, red
Für Abwärtskompatibilität mit Half Life 1 Quellcode kann immernoch Con_Printf()
und Con_DPrintf()
verwendet werden.
Variablen einbinden:
Float:
DevMsg("Diese Variable einbinden: %.2f", floatVariable)
Integer:
DevMsg("Diese Variable einbinden: %i", integerVariable)
String:
DevMsg("Diese Variable einbinden: %s", stringVariable)
Multiple Variable:
DevMsg("Binde das %i gefolgt von %s und %.2f ein", integerVariable, stringVariable, floatVariable)
Füge \n
ein, um Zeilenumbrüche zu erzeugen, um eine Zeile darunter etwas beim nächsten Aufruf von Msg/DevMsg auszugeben.
cvars setzen
[the_cvar]->SetValue( [value] );
Wenn es keinen Zugriff auf eine cvar gibt, muss dies vorher gemacht werden:
ConVar *[the_cvar] = cvar->FindVar( "[the_cvar]" );
Das funktioniert ebenfalls für cvars mit geschlossenem Quellcode.
Befehle ausführen
Die Engine liefert Schnittstellen für den Server- und Clientcode, um Befehle (Strings) aus dem Code heraus auszuführen, als würde der Benutzer diese eingeben haben. Der Server muss die Schnittstelle IVEngineServer::ServerCommand()
verwenden:
engine->ServerCommand("changelevel de_dust\n");
Clientcode muss die Schnittstelle IVEngineClient
verwenden und kann hier zwischen 2 Funktionen entscheiden, abhängig davon, ob der Befehl erst auf dem client ausgeführt oder direkt an den Server geschickt werden soll:
engine->ServerCmd( "say Hallo\n" ); // sendet den Befehl zum Server
oder
engine->ClientCmd( "say Hallo\n" ); // führt den Befehl auf dem Client aus
Wenn ein Serverkonsolenbefehl vom Client aus ausgeführt werden soll:
engine->ClientCommand( edict(), "command", ... );
edict() ist des Entities edict, und "command" würde durch den Konsolenbefehl ersetzt werden, gefolgt durch beliebige formatierte Variablen (vgl. printf). Dies wäre für das Anzeigen eines Teamauswahlmenüs nützlich.
Neue Befehle und Variablen hinzufügen
Die Entwicklerkonsole ist ein Subsystem, welches durch die Source Engine zur Verfügung gestellt wird und ist für alle anderen Module über die Schnittstelle ICvar
(siehe \public\icvar.h
) zugreifbar. Diese Schnittstelle erlaubt das Registrieren neuer Befehle und das Finden/Iterieren über bestehende Befehle. Diese Schnittstelle ist über den globalen CVAR Ingame Server-/Client-code (cv in Enginecode) verfügbar. Konsolenbefehle werden in ConCommand
implementiert und Konsolenvariablen in ConVar
, welche beide von der Basisklasse ConCommandBase
(siehe \public\convar.h
) abgeleitet sind.
Einen neuen Befehl oder eine neue Variable hinzuzufügen ist ziemlich einfach und benötigt den gleichen Code für Server- und Client- (auch Engine-) Module. Der Konstruktor dieser Klassen registriert automatisch den Befehl im Konsolensystem. Dieser kurze Beispielcode fügt den neuen Befehl my_function und die neue, mit 42 initialisierte Variable my_variable hinzu:
#include <convar.h>
ConVar my_variable( "my_variable", "42", FCVAR_ARCHIVE, "Meine Lieblingsnummer");
void MyFunction_f( void )
{
Msg("Das ist meine Funktion\n");
}
ConCommand my_function( "my_function", MyFunction_f, "Zeige eine Nachricht.", FCVAR_CHEAT );
Es ist üblich, dass der Objektname und der Befehlsname gleich sind und Variablen und variablen, die nur in einer einzigen Quelldatei verwendet werden, als statisch zu deklarieren.
Die ConVar-Klasse verwenden
ConVars speichern variablen, die über die Konsole verändert werden können und (optional) in config.cfg gespeichert werden. Der meistbenutze Konstruktor:
ConVar( char const *pName,
char const *pDefaultValue,
int flags,
char const *pHelpString )
Das erste Argument pName
ist der Variablenname (keine Leerzeichen), gefolgt durch pDefaultValue
, welches immer als String übergeben wird, auch bei ConVars mit numerischen Werten. Flags
bestimmen besondere Charakteristiken der Variable — alle Flagdefinitionen beginnen mit einem FCVAR_
-Präfix. Mehr Informationen über diese Flags können unten gefunden werden. Es ist immer gut, pHelpString
anzugeben, damit Benutzer eine vorstellung darüber bekommen, wofür die Variable da ist. ConVars sind nicht auf bestimmte Typen beschränkt. Ihr Typ kann eine Ganzzahl, eine Fließkommazahl, ein String oder ein beliebiger anderer sein. Solange das ConVar-Objekt selbst oder ein Pointer darauf vorliegt, kann der Wert direkt angepasst werden. All diese Beispiele sind gültig und haben das gleiche Resultat:
if ( my_variable.GetInt() == 42 ) DoSomething();
if ( my_variable.GetFloat() == 42.0f ) DoSomething();
if ( strcmp(my_variable.GetString(), "42")==0 ) DoSomething();
Um den Wert einer ConVar zu setzen, sollte die SetValue()
-Funktion verwendet werden, welche ebenfalls alle Datentypen erlaubt:
my_variable.SetValue( 42 );
my_variable.SetValue( 42.0f );
my_variable.SetValue( "42" );
Zu jeder Zeit kann eine ConVar über die Revert()
-Funktion zum Standardwert zurückgesetzt werden.
Wenn eine ConVar in einem anderen Modul erzeugt wird, kann die FindVar()
-Funktion der Schnittstelle ICvar
verwendet werden, um einen Pointer auf das Objekt zu erhalten, wenn der Variablenname bekannt ist. Das ist eine teure Suchfunktion und der Pointer sollte zwischengespeichert werden, wenn er häufiger wiederverwendet wird. Hier ist ein Beispiel, wie man die im Enginemodul definierte ConVar sv_cheats prüft:
ConVar *pCheats = cvar->FindVar( "sv_cheats" );
if ( pCheats && pCheats->GetInt() == 1 ) AllowCheating();
Ein Wertebereich kann für numerische ConVars über einen anderen Konstruktor bestimmt werden. Dann wird eine ConVar automatisch vom Konsolensystem geprüft, wenn sie manuell geändert wird. Wenn die eingegebene Nummer außerhalb des Wertebereichs liegt, wird sie zum nächstgelegenen gültigen Wert gerundet. Festlegen eines Bereichs von 1 bis 100:
ConVar my_variable( "my_variable", "42", 0, "Hilfetext", true, 1, true, 100 );
Manchmal ist eine Benachrichtigung, wenn der Benutzer oder ein anderes Modul den Wert der ConVar ändert. Dafür kann eine Callback-Funktion eingerichtet werden:
static void OnChangeMyVariable ( IConVar *var, const char *pOldValue, float flOldValue )
{
DevMsg( "ConVar %s wurde von %s in %s geändert\n", var->GetName(), pOldString, (( ConVar* )var)->GetString() );
}
ConVar my_variable( "my_variable", "42", 0, "Meine Lieblingsnummer", OnChangeMyVariable );
Die ConCommand-Klasse benutzen
Die Klasse
ConCommand speichert keine Werte, aber führt stattdessen eine Prozedur aus, sobald sie aufgerufen wird. Es ist einfacher, als die ConVar und besitzt nur einen Konstruktor:
ConCommand( char const *pName,
FnCommandCallback callback,
char const *pHelpString = 0,
int flags = 0,
FnCommandCompletionCallback completionFunc = 0 );
Wie bei der ConVar bestimmt pName
den Befehlsnamen (keine Leerzeichen). callback
ist die Funktion, die ausgeführt wird, wenn ein Benutzer den Befehl ausführt. pHelpString
und flags
haben die gleiche Funktionalität wie bei der ConVar. ConCommands Unterstützen Autovervollständigung für den ersten Parameter, was speziell für Befehle nützlich ist, die mit Dateien arbeiten. Wenn es beispielsweise den Befehl loadtext <textfile>
gibt, der eine .txt-Datei als Eingabe erwartet, wird die Konsole alle verfügbaren .txt-Dateien suchen und dem Benutzer eine Auswahllist zur Verfügung stellen. Wenn eine gültige completionFunc
übergeben wurde, wird diese immer aufgerufen, wenn das konsolensystem eine Liste verfügbarer Argumente benötigt.
Wenn die callback
-Funktion ausgeführt wird, werden die Parameter nicht als Funktionsargumente übergeben. Die callback
-Funktion muss die Engine mit Hilfe der Schnittstellenfunktion args.ArgC()
fragen, wie viele Argumente übergeben wurden, wobei 'args' der Name des CCommand s im Funktionskopf ist. Dann kann nach einzelnen Argumenten mit args.Arg(index)
geschaut werden, wobei 1 der index des ersten Arguments ist. Die Argumente werden immer als String zurückgeliefert.
void MySay_f ( const CCommand &args )
{
if ( args.ArgC() < 1 | args.Arg(1) == "" )
{
Msg("Benutzung: my_say <text>\n");
return;
}
Msg("Ich sage: %s\n", args.Arg(1) );
}
ConCommand my_say( "my_say", MySay_f , "Sag etwas", 0);
Hier ein Beispiel, wie eine einfache Autovervollständigungsliste gebaut wird. Der partial-Parameter wird hier nicht genutzt; er beinhaltet die bisher eingegebenen Zeichen (einschließlich des Befehlsnamensselbst):
static int MySayAutoComplete ( char const *partial,
char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] )
{
strcpy( commands[0], "Hallo" );
strcpy( commands[1], "Tschüss" );
return 2; // number of entries
}
ConCommand my_say( "my_say", MySay_f, "Sag etwas", 0, MySayAutoComplete);
Um den aufrufenden Spieler eines ConCommands abzurufen, kann UTIL_GetCommandClient()
auf dem Server oder C_BasePlayer::GetLocalPlayer()
auf dem Client verwendet werden.
Spieler-Clientbefehle
Aller Serverseitigen, von CBasePlayer abgeleiteten Spielerklassen haben die Funktion ClientCommand, welche aufgerufen wird, wenn ein Spieler einen ConCommand aufruft. Diese funktion kann zum Handhaben von ConCommands verwendet werden.
bool CHL2MP_Player::ClientCommand( const char *pcmd )
{
if ( FStrEq( pcmd, "spectate" ) )
{
if ( ShouldRunRateLimitedCommand( pcmd ) )
{
// sofort den Zuschauern beitreten
HandleCommand_JoinTeam( TEAM_SPECTATOR );
}
return true;
}
else if ( FStrEq( pcmd, "jointeam" ) )
{
if ( engine->Cmd_Argc() < 2 )
{
Warning("Spieler verwendete ungültige 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 );
}
Wie in diesem HL2MP Beispielcode zu sehen ist, wird die Funktion mit dem Argument pcmd aufgerufen, welche dann unter bestimmten Bedingungen behandelt wird. Die funktion ShouldRunRateLimitedCommand wird verwendet, um Spamming zu vermeiden und um zu prüfen, und um sicher zu gehen, dass der Befehl nicht zu häufig aufgerufen wurde.
Diese Funktion sieht in von der Orange Box abgeleitetem Code etwas anders aus.
Die FCVAR-Flags
Die Konsolenbefehl- und -variablenflags können ziemlich mächtige Charakteristiken aufweisen und sollten mit Vorsicht verwendet werden. Die Flags werden in der Regel im Konstruktor gesetzt, können aber auch mit ConCommandBase::AddFlags()
(nicht sehr häufig benutzt) modifiziert werden. Es ist nicht möglich, die Flags auf anderem Wege als über Quellcode zu ändern, um Cheating zu vermeiden.
Manche Flags müssen manuell gesetzt werden, aber die unten aufgeführten werden automatisch durch das Konsolensystem gesetzt:
- FCVAR_LAUNCHER
- FCVAR_GAMEDLL
- FCVAR_CLIENTDLL
- FCVAR_MATERIAL_SYSTEM
- FCVAR_STUDIORENDER
Die übrigen Flags müssen manuell gesetzt werden:
FCVAR_CHEAT
- Die meisten Befehle und Variablen sind für debugzwecke gedacht und werden in Releaseversionen nicht entfernt, da sie ebenfalls für 3. Entwickler und Mapersteller nützlich sind. Unglücklicherweise kann die Benutzung dieser Debugging-Tools normalen Spielern nicht erlaubt werden, da es ein unfairer Vorteil anderen Spielern gegenüber wäre (Cheating). Eine gute Regel ist es,
FCVAR_CHEAT
zu grundsätzlich jedem neuen Konsolenbefehl hinzuzufügen, es sei denn, ess sind explizite und legitime Optionen für den Spieler. Erfahrung zeigt, dass auch die harmlosesten Debugbefehle irgendwie als Cheats missbraucht werden können. - Die Servereinstellung von
sv_cheats
entscheidet, ob cheats erlaubt sind oder nicht. Wenn ein Client sich mit einem Server verbindet, auf dem Cheats deaktiviert sind (sollte der Standardfall sein), werden alle Clientseitigen Konsolenvariablen, die alsFCVAR_CHEAT
markiert sind, auf ihren Standardwert zurückgesetzt und können nicht verändert werden, solange der Client verbunden bleibt. Konsolenbefehle, die alsFCVAR_CHEAT
markiert sind, können ebenfalls nicht ausgeführt werden. FCVAR_SERVER_CAN_EXECUTE
- Das Markieren eines Befehls als FCVAR_SERVER_CAN_EXECUTE sorgt dafür, dass nur der Server dazu erlaubt ist, diesen zu benutzen (die srcds-Konsole oder der Spieler, der den lokalen Serber erstellt hat).
FCVAR_NOT_CONNECTED
- Manche Konsolenvariablen sollten nicht änderbar sein, solange ein Client auf einem Server ist (z. B. fps_max), da der Befehl sonst ausgenutzt werden könnte.
FCVAR_USERINFO
- Manche Konsolenvariablen enthalten Clientinformationen, die der Server wissen sollte, wie der Name des Spielers oder seine Netzwerkeinstellungen. Diese müssen als
FCVAR_USERINFO
markiert werden, damit sie zum Server übertragen werden und jedes mal aktualisiert werden, wenn der Benutzer sie ändert. Wenn der Spieler eine der Variablen ändert, benachrichtigt die Engine den Servercode mittelsClientSettingsChanged()
. Dann kann der Server den Client nach bestimmten Cleinteinstellungen mitGetClientConVarValue()
abfragen. FCVAR_REPLICATED
- Server und Client benutzen geteilten Code, bei dem es wichtig ist, dass beide Seiten genau den gleichen Pfad mit den gleichen Daten ausführen (z. B. vorhergesagt Bewegung/Waffen, GameRules). Wenn dieser Code Konsolenvariablen benutzt, müssen diese auf beiden Seiten dem gleichen Wert entsprechen. Der Flag
FCVAR_REPLICATED
stellt dies durch die Sendung der Werte an alle Clients sicher. solange sie verbunden sind, können Clients diese Werte nicht ändern und sind zur Nutzung den Serverseitigen Werten gezwungen. FCVAR_ARCHIVE
- Manche Konsolenvariablen enthalten bestimmte Einstellungen, die jedes Mal wiederhergestellt werden sollen, wenn das Spiel gestartet wird (wie
name
odernetwork_rate
). Wenn eine Konsolenvariable alsFCVAR_ARCHIVE
markiert ist, wird sie in der dateiconfig.cfg
gespeichert wenn das Spiel beendet wird und wird beim nächsten Start geladen. (Auch der Befehlhost_writeconfig
speichert alleFCVAR_ARCHIVE
-Variablen in eine Datei). FCVAR_NOTIFY
- Wenn eine Konsolenvariable als
FCVAR_NOTIFY
markiert ist, sendet ein Server immer dann Benachrichtigungen zu allen Clients, wenn die Variable verändert wird. Das sollte für Variablen verwendet werden, die die Gameplay-Regeln ändern, welche für alle Spieler wichtig sind (mp_friendlyfire
etc.). FCVAR_PROTECTED
- Wenn Konsolenvariablen private Informationen beinhalten (Passwörter etc.), Sollen sie nicht für andere Spieler sichtbar sein. Dann sollte das
FCVAR_PROTECTED
-Flag gesetzt werden, um die Information als vertraulich zu markieren. FCVAR_SPONLY
- Manchmal sollen Befehlsausführungen oder Variablenänderungen nur im Singleplayer gültig sein. In diesem Fall können diese Befehle als
FCVAR_SPONLY
markiert werden. FCVAR_PRINTABLEONLY
- Manche wichtigen Variablen werden geloggt und an alle Clients gesendet (GameRules etc.) und es ist wichtig, dass diese nur darstellbare Zeichen beinhalten (keine Steuerzeichen etc).
FCVAR_NEVER_AS_STRING
- Das
FCVAR_NEVER_AS_STRING
-Flag teilt der Engine mit, diese Variablen niemals als String auszugeben, da sie Steuerzeichen benihalten. FCVAR_DEMO
- Wenn die aufnahme einer Demo-Datei angefangen wird, müssen manche Konsolenvariablen explizit zur Aufnahme hinzugefügt werden, um eine richtige Wiedergabe zu gewährleisten.
FCVAR_DONTRECORD
- Das ist das Gegenteil von FCVAR_DEMO. Manche Konsolenbefehle sollen nicht in eine Demo-Datei aufgenommen werden.