Not exactly a tutorial in the sense of tutorials, but there are comments related to each bit of code, and how\why they're used.
It also basically shows how you can use ClientCmd without needing the Source SDK to have the engine pointer.
To update the Engine_PTR (the only thing would ever need to be updated for this ) you need only a debugger - and client.dll, all of which are easily acquired.
For OllyDbg, for example, you could search for referenced string texts in the module Client.dll, and then search for text in the entire scope for "host_timescale", the very first result will bring you to where you need to be:
Code:
380F1E30 . 8B0D 04318E38 MOV ECX,DWORD PTR DS:[388E3104]
380F1E36 . 8B01 MOV EAX,DWORD PTR DS:[ECX]
380F1E38 . 8B50 1C MOV EDX,DWORD PTR DS:[EAX+1C]
380F1E3B . 68 A4CC7A38 PUSH client.387ACCA4 ; ASCII "host_timescale 0.0001"
Here, we see our current engine pointer, 0x388E3104:
Code:
MOV ECX,DWORD PTR DS:[388E3104]
Which is the one we are using now, in this source.
This should work for every update, that is, until they attempt to patch it.
In which case, a new way will be made.
Plenty of things can be done with this, like spamming certain commands
without smashing a binded key repeatedably, via another thread, and
toggleable booleans.
It also doesn't have the drawback of requiring the Source SDK.
Code:
// ------------------- defines
#define Engine_PTR 0x388E3104 // Our pointer to the engine - address is current
// ------------------- defines
#include <windows.h> // The only include file we need. No Source SDK or anything.
// globals
bool g_bEngineReady = false; // Used to know if the engine pointer is ready to use or not.
DWORD g_dwEnginePtr = NULL; // Pointer to engine, setup when game is loaded.
// globals
// class
// Not fully mapped, at least not the vftable functions.
// The only really important one is ClientCmd, anyways.
struct IVGameEngine
{
public:
virtual void A(); // 0
virtual void B(); // 4
virtual void C(); // 8
virtual void D(); // C
virtual void E(); // 10
virtual void F(); // 14
virtual void G(); // 18
virtual void ClientCmd( char * pszCommand ); // 1C - ClientCmd
virtual void H(); // 20
virtual void I(); // 24
virtual void J(); // 28
virtual void K(); // 2C
virtual void L(); // 30
virtual void M(); // 34
virtual void N(); // 38
virtual void O(); // 3C
virtual void P(); // 40
virtual void Q(); // 44
virtual void R(); // 48
virtual void S(); // 4C
virtual void T(); // 50
virtual void U(); // 54
virtual void V(); // 58
virtual void W(); // 5C
virtual void X(); // 60
virtual void Y(); // 64
virtual void Z(); // 68
virtual void AA(); // 6C
virtual void BB(); // 70
virtual void CC(); // 74
virtual void DD(); // 78
virtual int Cmd_Argc(); // 7C - Cmd_Argc, returns # of commands passed to ClientCmd
virtual void * Cmd_Argv( int iIndex ); // 80 - Cmd_Argv, returns command arguments
// based on iIndex.
};
// class
// our engine pointer - not actually instantiated by us - since they're all virtual functions
IVGameEngine * g_pGameEngine = NULL;
// ------------------------------------------------------------------------------------------
// thread to create, testthread
void ThreadTest( )
{
while( 1 )
{
if( GetAsyncKeyState( 0x61 ) & 1 ) // Numpad 1 key check, when
// you press Numpad 1 (only once) the engine pointer is setup.
{
// This is by no means the best way to do this
g_dwEnginePtr = *(PDWORD)( Engine_PTR ); // Access engine
// pointer vftable.
if( g_dwEnginePtr ) // if it != 0
{
g_pGameEngine = (IVGameEngine*)( (DWORD)g_dwEnginePtr );
// Here, (above this comment) we point our pointer to
// the current engine pointer in the game.
g_bGameEngineReady = true; // Here, we set the variable
// g_bGameEngineReady to true, so that we know
// it's ok to call ClientCmd.
}
}
if( GetAsyncKeyState( 0x62 ) & 1 ) // Numpad 2 key check, when
// you press Numpad 2 (only once) the commands you want to use
// will be executed. You can also set convars here, if you want.
{
if( g_bGameEngineReady ) // Check if game engine pointer was
// setup.
{
g_pGameEngine->ClientCmd("commands here\n"); // Issue a
// command.
}
}
// This is also a way that you could make keybinds of your own,
// without using bind. Even unbindable keys, like escape.
// A google search for virtual key codes will bring you to the
// constants required to be passed to GetAsyncKeyState.
Sleep( 50 );
}
}
// ------------------------------------
// DLLMain, entrypoint for our DLL.
BOOL APIENTRY DllMain( HMODULE hModule, DWORD dwReasonForCall,
LPVOID lpReserved
)
{
switch ( dwReasonForCall )
{
case DLL_PROCESS_ATTACH:
if( CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)&TestThread, 0, 0, INVALID_HANDLE_VALUE )
// Notify failure to create test thread, if you want
OutputDebugStringA( " Couldn't create TestThread " );
break;
}
return TRUE;
}