The netvar utility provides runtime offset resolution for networked entity properties in the Source Engine. It traverses the client’s networked data tables to find variable offsets dynamically.
Overview
Netvars (network variables) are properties synchronized between the server and client. The netvar system allows you to find the memory offset of these properties at runtime, making your code compatible across game updates.
// Find offset of m_iHealth in DT_BasePlayer
uintptr_t healthOffset = netvar::get_table("DT_BasePlayer", "m_iHealth");
// Use in entity class
int health = *reinterpret_cast<int*>(reinterpret_cast<uintptr_t>(player) + healthOffset);
Functions
get_table
Find the offset of a networked variable by table and variable name.
std::uintptr_t get_table(const char* table, const char* var)
The name of the networked data table (e.g., “DT_BasePlayer”)
The name of the variable within the table (e.g., “m_iHealth”)
The offset of the variable, or 0 if not found
// Common CS:GO netvars
auto healthOffset = netvar::get_table("DT_BasePlayer", "m_iHealth");
auto teamOffset = netvar::get_table("DT_BaseEntity", "m_iTeamNum");
auto flagsOffset = netvar::get_table("DT_BasePlayer", "m_fFlags");
auto velocityOffset = netvar::get_table("DT_BasePlayer", "m_vecVelocity[0]");
get_offset
Recursively search a recv_table for a variable offset.
std::uintptr_t get_offset(sdk::recv_table* table, const char* var)
Pointer to a recv_table to search
The variable name to find
The offset of the variable within the table
This function is used internally by get_table(). You typically don’t need to call it directly.
How It Works
The netvar system works by:
- Retrieving all client classes from the client interface
- Searching for the specified table name using compile-time hashing
- Recursively traversing nested data tables
- Accumulating offsets through the table hierarchy
- Returning the final offset when the variable is found
// Simplified flow:
// DT_CSPlayer
// ├─ DT_BasePlayer (offset: 0x00)
// │ ├─ m_iHealth (offset: 0x100)
// │ └─ m_fFlags (offset: 0x104)
// └─ m_bHasDefuser (offset: 0x200)
// get_table("DT_BasePlayer", "m_iHealth") returns 0x100
// get_table("DT_CSPlayer", "m_bHasDefuser") returns 0x200
Common Netvars
Base Entity
namespace offsets {
inline uintptr_t m_iTeamNum = netvar::get_table("DT_BaseEntity", "m_iTeamNum");
inline uintptr_t m_vecOrigin = netvar::get_table("DT_BaseEntity", "m_vecOrigin");
inline uintptr_t m_nModelIndex = netvar::get_table("DT_BaseEntity", "m_nModelIndex");
}
Base Player
namespace offsets {
inline uintptr_t m_iHealth = netvar::get_table("DT_BasePlayer", "m_iHealth");
inline uintptr_t m_lifeState = netvar::get_table("DT_BasePlayer", "m_lifeState");
inline uintptr_t m_fFlags = netvar::get_table("DT_BasePlayer", "m_fFlags");
inline uintptr_t m_vecVelocity = netvar::get_table("DT_BasePlayer", "m_vecVelocity[0]");
inline uintptr_t m_vecViewOffset = netvar::get_table("DT_BasePlayer", "m_vecViewOffset[0]");
inline uintptr_t m_hActiveWeapon = netvar::get_table("DT_BasePlayer", "m_hActiveWeapon");
}
CS Player
namespace offsets {
inline uintptr_t m_bHasDefuser = netvar::get_table("DT_CSPlayer", "m_bHasDefuser");
inline uintptr_t m_bHasHelmet = netvar::get_table("DT_CSPlayer", "m_bHasHelmet");
inline uintptr_t m_ArmorValue = netvar::get_table("DT_CSPlayer", "m_ArmorValue");
inline uintptr_t m_iAccount = netvar::get_table("DT_CSPlayer", "m_iAccount");
inline uintptr_t m_bIsScoped = netvar::get_table("DT_CSPlayer", "m_bIsScoped");
inline uintptr_t m_bIsDefusing = netvar::get_table("DT_CSPlayer", "m_bIsDefusing");
}
Weapon
namespace offsets {
inline uintptr_t m_iClip1 = netvar::get_table("DT_BaseCombatWeapon", "m_iClip1");
inline uintptr_t m_iClip2 = netvar::get_table("DT_BaseCombatWeapon", "m_iClip2");
inline uintptr_t m_flNextPrimaryAttack = netvar::get_table("DT_BaseCombatWeapon", "m_flNextPrimaryAttack");
inline uintptr_t m_hOwner = netvar::get_table("DT_BaseCombatWeapon", "m_hOwner");
}
Usage Patterns
Creating Offset Namespace
namespace offsets {
void initialize() {
console::print<console::log_level::NORMAL>("Initializing netvars...");
// Base entity
m_iTeamNum = netvar::get_table("DT_BaseEntity", "m_iTeamNum");
m_vecOrigin = netvar::get_table("DT_BaseEntity", "m_vecOrigin");
// Base player
m_iHealth = netvar::get_table("DT_BasePlayer", "m_iHealth");
m_fFlags = netvar::get_table("DT_BasePlayer", "m_fFlags");
console::print<console::log_level::SUCCESS>("Netvars initialized");
}
inline uintptr_t m_iTeamNum;
inline uintptr_t m_vecOrigin;
inline uintptr_t m_iHealth;
inline uintptr_t m_fFlags;
}
Using Netvars in Entity Classes
class c_base_entity {
public:
int team() {
static auto offset = netvar::get_table("DT_BaseEntity", "m_iTeamNum");
return *reinterpret_cast<int*>(reinterpret_cast<uintptr_t>(this) + offset);
}
math::vec3 origin() {
static auto offset = netvar::get_table("DT_BaseEntity", "m_vecOrigin");
return *reinterpret_cast<math::vec3*>(reinterpret_cast<uintptr_t>(this) + offset);
}
};
class c_base_player : public c_base_entity {
public:
int health() {
static auto offset = netvar::get_table("DT_BasePlayer", "m_iHealth");
return *reinterpret_cast<int*>(reinterpret_cast<uintptr_t>(this) + offset);
}
int flags() {
static auto offset = netvar::get_table("DT_BasePlayer", "m_fFlags");
return *reinterpret_cast<int*>(reinterpret_cast<uintptr_t>(this) + offset);
}
bool is_alive() {
return health() > 0;
}
bool is_on_ground() {
return flags() & FL_ONGROUND;
}
};
Macro-Based Netvar Definition
#define NETVAR(func_name, type, table, var) \
type& func_name() { \
static auto offset = netvar::get_table(table, var); \
return *reinterpret_cast<type*>(reinterpret_cast<uintptr_t>(this) + offset); \
}
class c_cs_player : public c_base_player {
public:
NETVAR(has_defuser, bool, "DT_CSPlayer", "m_bHasDefuser")
NETVAR(has_helmet, bool, "DT_CSPlayer", "m_bHasHelmet")
NETVAR(armor_value, int, "DT_CSPlayer", "m_ArmorValue")
NETVAR(account, int, "DT_CSPlayer", "m_iAccount")
NETVAR(is_scoped, bool, "DT_CSPlayer", "m_bIsScoped")
};
// Usage
auto player = get_player(index);
if (player->has_defuser()) {
console::print<console::log_level::DEBUG>("Player has defuse kit");
}
Validating Netvars
bool validate_netvars() {
struct netvar_entry {
const char* table;
const char* var;
const char* description;
};
std::vector<netvar_entry> required_netvars = {
{"DT_BasePlayer", "m_iHealth", "Player health"},
{"DT_BasePlayer", "m_fFlags", "Player flags"},
{"DT_BaseEntity", "m_iTeamNum", "Team number"},
{"DT_CSPlayer", "m_bHasDefuser", "Has defuser"},
};
bool all_valid = true;
for (const auto& entry : required_netvars) {
auto offset = netvar::get_table(entry.table, entry.var);
if (offset == 0) {
console::print<console::log_level::FATAL>(
"Failed to find netvar: {}.{} ({})",
entry.table, entry.var, entry.description
);
all_valid = false;
} else {
console::print<console::log_level::DEBUG>(
"Found {}.{} at offset {:#x}",
entry.table, entry.var, offset
);
}
}
return all_valid;
}
Advanced Usage
Caching Netvars
class netvar_cache {
public:
static uintptr_t get(const char* table, const char* var) {
auto hash = HASH(table) ^ HASH(var);
if (auto it = cache_.find(hash); it != cache_.end())
return it->second;
auto offset = netvar::get_table(table, var);
cache_[hash] = offset;
return offset;
}
private:
static inline std::unordered_map<uint32_t, uintptr_t> cache_;
};
Best Practices
Cache netvar offsets using static variables to avoid repeated lookups.
Validate critical netvars during initialization to catch issues early.
Netvar names are case-sensitive and must match exactly as they appear in the game’s data tables.
Array netvars use bracket notation: “m_vecVelocity[0]” for the first element.
Use compile-time hash functions (HASH macro) for string comparisons to improve performance.