- Status
- Offline
- Joined
- Mar 3, 2026
- Messages
- 690
- Reaction score
- 457
Still digging into GoldSrc? Finding the entity list in Counter-Strike 1.6 isn't exactly rocket science, but if you are tired of searching or just spinning up a new internal base, here is the direct path through hw.dll.
Locating the Entry Point
Throw hw.dll into IDA Pro. The most reliable way to find where the engine handles entities and client counts is by searching for specific formatted strings used in debugging or console prints.
Technical Offsets and Signatures
From a typical build, you are looking for these offsets (though addresses will vary by version, the logic holds):
If you prefer signature scanning to avoid hardcoded offsets, use these patterns:
Accessing Player Data (pvPrivateData)
The offset points to the edict_t structure. To get the actual player data (like health, armor, etc.), you need to access the pvPrivateData pointer, which usually points to the CBasePlayer class in the game logic.
Client Data Structure
For those working on the visual side or movement exploits, here is the clientdata_t struct for reference. This is what you'll be parsing once you have the right pointers:
Note that the max_clients value is typically sitting 4 bytes away from the entity pointer start. Standard value is 32 for most servers. If you are hitting nullptrs, double-check your pvPrivateData offset, as it can shift between different engine builds.
Anyone tested these signatures on the latest Steam build lately?
Locating the Entry Point
Throw hw.dll into IDA Pro. The most reliable way to find where the engine handles entities and client counts is by searching for specific formatted strings used in debugging or console prints.
- Search for the string:
Code:
"%s" %s %s %d %d %d %d\n - Check the cross-references (Xrefs) for this string.
- Scroll up from the xref location. You should see the entity list pointer and the max clients variable sitting right next to each other.
Technical Offsets and Signatures
From a typical build, you are looking for these offsets (though addresses will vary by version, the logic holds):
- Entity List Pointer: dword_807588
- Max Clients: dword_807584
- Stride/Size:
Code:
add ebx, 5018h
If you prefer signature scanning to avoid hardcoded offsets, use these patterns:
Code:
// Signature for A36B4
A1 ? ? ? ? 53 8B 1D ? ? ? ? 56 85 C0
// Signature for A36BA
8B 1D ? ? ? ? 56 85 C0
Accessing Player Data (pvPrivateData)
The offset points to the edict_t structure. To get the actual player data (like health, armor, etc.), you need to access the pvPrivateData pointer, which usually points to the CBasePlayer class in the game logic.
Code:
// Access CBasePlayer via edict_t
uintptr_t pCBasePlayer = *(uintptr_t*)(pEdict + 0x4B9C); // 0x4B9C is pvPrivateData
// Read health from the private data (offset 0x1E0)
int health = *(int*)(pCBasePlayer + 0x1E0);
Client Data Structure
For those working on the visual side or movement exploits, here is the clientdata_t struct for reference. This is what you'll be parsing once you have the right pointers:
Code:
struct clientdata_t
{
std::uint8_t pad[0x88];
vec3_t origin;
vec3_t velocity;
int viewmodel;
vec3_t punchangle;
int flags;
int waterlevel;
int watertype;
vec3_t view_ofs;
float health;
int b_in_duck;
int weapons;
int fl_time_step_sound;
int fl_duck_time;
int fl_swim_time;
int waterjumptime;
float maxspeed;
float fov;
int weaponanim;
int m_i_id;
int ammo_shells;
int ammo_nails;
int ammo_cells;
int ammo_rockets;
float m_fl_next_attack;
int tfstate;
int pushmsec;
int deadflag;
char physinfo[64];
int iuser1;
int iuser2;
int iuser3;
int iuser4;
float fuser1;
float fuser2;
float fuser3;
float fuser4;
vec3_t vuser1;
vec3_t vuser2;
vec3_t vuser3;
vec3_t vuser4;
};
Note that the max_clients value is typically sitting 4 bytes away from the entity pointer start. Standard value is 32 for most servers. If you are hitting nullptrs, double-check your pvPrivateData offset, as it can shift between different engine builds.
Anyone tested these signatures on the latest Steam build lately?