Creating your own HLDJ commands
Posted: Wed Nov 24, 2010 9:12 pm
** code featured in this topic is highly subject to change!
Now that HLDJ's source is out and in the open, the natural question is "how can I include my own HLDJ commands?".
Here is an example of how to include just such a custom command that outputs a random message to console along with date & time.
* For most purposes, all modifications can be done in src/hldj_api.c.
Firstly, head over to the function hldj_CreateCommandScanThread, and add your command (string) and an identifier (integer) to the command dictionary:
Typically, you want to define these in an included header file, such as hldj.h or one you include from hldj.h:
Next, in hldj_WriteHLDJConfig, add your command to the game as an alias:
You can place this towards the end of the function, after all the HLDJ commands have been defined, or better still, you can generate your own initialization script, and call it from HLDJ's.
Notice the script you exec at the end? It is the same you defined earlier, and will be used to communicate back to the game. The general flow of sending commands is: send your command to HLDJ (by binding it to the command relay key and writing the config file), 'wait' a bit while HLDJ processes it, then 'exec' some output file that you will generate in HLDJ.
Finally, you need to handle your command in HLDJ (including generating the output file). This is done in handleCommand_ThreadFunc, add a case to handle your command's id:
In this example, we're going to implement do_stuff to generate a random message and display the date & time.
(but in theory you could have it do anything you want! from retrieving stats from a website, to starting a command on your computer, to controlling something remotely on another computer)
Compiling & Building HLDJ
The HLDJ source can be compiled using any C99-compliant compiler (GNU GCC preferred). To build the entire project, it is recommended to have the MingW64 compiler tools (along with a 'make' utility) on path. Modify the makefile in the indicated place (under the "# compiler" section) to match your specific compiler name & prefix. Notice the prefixes are different for 32- and 64-bit builds, this allows you to have both 32- and 64-bit compilers on path with different prefixes.
For example, you will notice from the makefile the compiler base name is 'gcc', but the 32-bit prefix is 'i686-w64-mingw32-' and 64-bit prefix is 'x86_64-w64-mingw32-' (as per the MingW64 compilers). Again you should modify these to whatever makes sense for your compilation environment so that the makefile has no problems invoking your compiler.
To start the build process, run 'make' on the command line from the hldj root directory.
If you have any questions, comments, corrections, or suggestions, feel free to add them to this thread!
Now that HLDJ's source is out and in the open, the natural question is "how can I include my own HLDJ commands?".
Here is an example of how to include just such a custom command that outputs a random message to console along with date & time.
* For most purposes, all modifications can be done in src/hldj_api.c.
Firstly, head over to the function hldj_CreateCommandScanThread, and add your command (string) and an identifier (integer) to the command dictionary:
Code: Select all
hldj_commandScanThread_t *
hldj_CreateCommandScanThread( gameEnv_t *pGameEnv, void (*handleMessage)(int) )
{
...
rht_insert( thread->pCmdTrie, MY_CUSTOM_CMD, (void *)MY_CUSTOM_CMD_ID, !0 );
...
}
Code: Select all
#define MY_CUSTOM_CMD "my_custom_cmd"
#define MY_CUSTOM_CMD_ID 0xFF // make sure the id is unique
#define MY_CUSTOM_CMD_OUT_CFG "my_custom_cmd_out.cfg" // you'll generate this file later on, to communicate back to the game
Code: Select all
unsigned short
hldj_WriteHLDJConfig( const gameEnv_t *gameEnv, unsigned short numFileErrors )
{
...
/* my custom alias - this will pass the command to HLDJ, then exec a generated file */
fprintf( config, "\nalias "MY_CUSTOM_CMD" \""BINDCMD" %s "MY_CUSTOM_CMD"; "SELECTCMD"; exec "MY_CUSTOM_CMD_OUT_CFG"\"", gameEnv->game->commandRelayKey );
...
Notice the script you exec at the end? It is the same you defined earlier, and will be used to communicate back to the game. The general flow of sending commands is: send your command to HLDJ (by binding it to the command relay key and writing the config file), 'wait' a bit while HLDJ processes it, then 'exec' some output file that you will generate in HLDJ.
Finally, you need to handle your command in HLDJ (including generating the output file). This is done in handleCommand_ThreadFunc, add a case to handle your command's id:
Code: Select all
static unsigned __stdcall
handleCommand_ThreadFunc( void *pArgs )
{
...
switch( msg )
{
...
case MY_CUSTOM_CMD_ID:
my_custom_cmd_do_stuff( thread->pGameEnv ); break;
}
...
}
(but in theory you could have it do anything you want! from retrieving stats from a website, to starting a command on your computer, to controlling something remotely on another computer)
Code: Select all
#include <time.h>
// generates random message
void my_custom_cmd_do_stuff( const gameEnv_t *pGameEnv )
{
// first, build a path to the generated file
char fileName[ MAX_PATH+1 ];
strcpy( fileName, gameEnv->game->gamePath );
addTerminatingPathBackslash( fileName );
// if it's a Source game, the configs reside in the 'cfg\' folder
if( gameEnv->game->engine == HL2ENGINE )
strcat( fileName, CFGFOLDER );
strcat( fileName, MY_CUSTOM_CMD_CFG );
FILE *config = fopen( fileName, "w" );
if( config == NULL)
return 1;
// choose a random message and echo it to console
const char *msg[4] = { "Hello World", "Goodbye World", "foo", "bar" };
const int i = rand() % 4;
fprintf( config, "\necho \"%s\"", msg[i] );
// display the time
time_t rawtime;
struct tm * timeinfo;
time( &rawtime );
timeinfo = localtime( &rawtime );
fprintf ( config, "\necho \"Generated on: %s\"", asctime( timeinfo ) );
fclose( config );
}
Compiling & Building HLDJ
The HLDJ source can be compiled using any C99-compliant compiler (GNU GCC preferred). To build the entire project, it is recommended to have the MingW64 compiler tools (along with a 'make' utility) on path. Modify the makefile in the indicated place (under the "# compiler" section) to match your specific compiler name & prefix. Notice the prefixes are different for 32- and 64-bit builds, this allows you to have both 32- and 64-bit compilers on path with different prefixes.
For example, you will notice from the makefile the compiler base name is 'gcc', but the 32-bit prefix is 'i686-w64-mingw32-' and 64-bit prefix is 'x86_64-w64-mingw32-' (as per the MingW64 compilers). Again you should modify these to whatever makes sense for your compilation environment so that the makefile has no problems invoking your compiler.
To start the build process, run 'make' on the command line from the hldj root directory.
If you have any questions, comments, corrections, or suggestions, feel free to add them to this thread!