summaryrefslogtreecommitdiffstats
path: root/src/server.cpp
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-11-04 17:25:31 +0100
committersalmonik <salmonik@tuta.io>2026-11-04 17:25:31 +0100
commit710e8cc59557bc95ed74af8726ea13012fde50c3 (patch)
treef1fd242c12d2ce5a939fbf1d3828e3546bc8f1cf /src/server.cpp
parentNetvars: catch array element props and log the walk (diff)
downloadhl2h-710e8cc59557bc95ed74af8726ea13012fde50c3.tar.gz
hl2h-710e8cc59557bc95ed74af8726ea13012fde50c3.zip
Items: read ammo and inventory from the server arrays
The client m_iAmmo/m_hMyWeapons recv props still do not resolve, but the listen server has the same CBaseCombatCharacter arrays in its datamap. Resolve m_iAmmo and m_hMyWeapons there: - reserve ammo comes from server_player_ammo() when the client array is missing, so full-ammo filtering works (RPG rockets at 3); - the owned-weapon mask is filled from the server m_hMyWeapons[48] handles, so fallen weapons count as ammo before the player equips them. Widen the datamap field-size filter to 512 for the 192-byte handle array.
Diffstat (limited to 'src/server.cpp')
-rw-r--r--src/server.cpp35
1 files changed, 32 insertions, 3 deletions
diff --git a/src/server.cpp b/src/server.cpp
index 1313f00..af0a8ff 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -58,6 +58,8 @@ static int g_srv_mapname = -1;
58static int g_srv_armor = -1; 58static int g_srv_armor = -1;
59static int g_srv_detonate = -1; 59static int g_srv_detonate = -1;
60static int g_srv_grenade_radius = -1; 60static int g_srv_grenade_radius = -1;
61static int g_srv_ammo = -1;
62static int g_srv_myweapons = -1;
61static bool g_srv_ready = false; 63static bool g_srv_ready = false;
62static bool g_srv_checked = false; 64static bool g_srv_checked = false;
63 65
@@ -97,7 +99,7 @@ static int datamap_offset(const char *name, int want_type, int min_off = 0x40,
97 continue; 99 continue;
98 if (foff < min_off || foff > max_off) 100 if (foff < min_off || foff > max_off)
99 continue; 101 continue;
100 if (fsize < 1 || fsize > 128) 102 if (fsize < 1 || fsize > 512)
101 continue; 103 continue;
102 // DEFINE_KEYFIELD entries have an external name ("health"); plain 104 // DEFINE_KEYFIELD entries have an external name ("health"); plain
103 // DEFINE_FIELD entries have none. Either way it must be in server.so. 105 // DEFINE_FIELD entries have none. Either way it must be in server.so.
@@ -160,17 +162,22 @@ bool resolve_server_netvars() {
160 // CBaseGrenade fuse / blast radius (m_flDetonateTime is not networked). 162 // CBaseGrenade fuse / blast radius (m_flDetonateTime is not networked).
161 g_srv_detonate = datamap_offset("m_flDetonateTime", FIELD_TIME); 163 g_srv_detonate = datamap_offset("m_flDetonateTime", FIELD_TIME);
162 g_srv_grenade_radius = datamap_offset("m_DmgRadius", FIELD_FLOAT); 164 g_srv_grenade_radius = datamap_offset("m_DmgRadius", FIELD_FLOAT);
165 // CBaseCombatCharacter arrays: ammo counts and the weapon handle list.
166 g_srv_ammo = datamap_offset("m_iAmmo", FIELD_INTEGER, 0x40, 0x4000);
167 g_srv_myweapons = datamap_offset("m_hMyWeapons", FIELD_EHANDLE);
163 g_srv_ready = g_srv_health > 0; 168 g_srv_ready = g_srv_health > 0;
164 if (g_srv_ready) { 169 if (g_srv_ready) {
165 logf("server datamap (%s): health=%d maxhealth=%d lifeste=%d " 170 logf("server datamap (%s): health=%d maxhealth=%d lifeste=%d "
166 "modelindex=%d absvel=%d vel=%d activeweapon=%d relationship=%d " 171 "modelindex=%d absvel=%d vel=%d activeweapon=%d relationship=%d "
167 "followtarget=%d spawnflags=%d hacked=%d npcstate=%d " 172 "followtarget=%d spawnflags=%d hacked=%d npcstate=%d "
168 "takedamage=%d mapname=%d armor=%d detonate=%d dmgradius=%d", 173 "takedamage=%d mapname=%d armor=%d detonate=%d dmgradius=%d "
174 "ammo=%d myweapons=%d",
169 g_server.path.c_str(), g_srv_health, g_srv_maxhealth, g_srv_lifestate, 175 g_server.path.c_str(), g_srv_health, g_srv_maxhealth, g_srv_lifestate,
170 g_srv_modelindex, g_srv_absvelocity, g_srv_velocity, 176 g_srv_modelindex, g_srv_absvelocity, g_srv_velocity,
171 g_srv_activeweapon, g_srv_relationship, g_srv_followtarget, 177 g_srv_activeweapon, g_srv_relationship, g_srv_followtarget,
172 g_srv_spawnflags, g_srv_hacked, g_srv_npcstate, g_srv_takedamage, 178 g_srv_spawnflags, g_srv_hacked, g_srv_npcstate, g_srv_takedamage,
173 g_srv_mapname, g_srv_armor, g_srv_detonate, g_srv_grenade_radius); 179 g_srv_mapname, g_srv_armor, g_srv_detonate, g_srv_grenade_radius,
180 g_srv_ammo, g_srv_myweapons);
174 fail_logged = false; 181 fail_logged = false;
175 } else if (!fail_logged) { 182 } else if (!fail_logged) {
176 fail_logged = true; 183 fail_logged = true;
@@ -475,6 +482,28 @@ float server_grenade_radius(int idx) {
475 return *(float *)((uintptr_t)base + g_srv_grenade_radius); 482 return *(float *)((uintptr_t)base + g_srv_grenade_radius);
476} 483}
477 484
485// Server-side CBaseCombatCharacter::m_iAmmo array (reserve ammo).
486int server_player_ammo(int idx, int type) {
487 if (!g_srv_ready || g_srv_ammo < 0 || type < 0 || type >= 32)
488 return -1;
489 void *base = nullptr;
490 server_entity_health(idx, nullptr, nullptr, &base);
491 if (!base)
492 return -1;
493 return *(int *)((uintptr_t)base + g_srv_ammo + type * 4);
494}
495
496// Server-side m_hMyWeapons[48] handle for one inventory slot (0 if empty).
497uint32_t server_player_weapon(int idx, int slot) {
498 if (!g_srv_ready || g_srv_myweapons < 0 || slot < 0 || slot >= 48)
499 return 0;
500 void *base = nullptr;
501 server_entity_health(idx, nullptr, nullptr, &base);
502 if (!base)
503 return 0;
504 return *(uint32_t *)((uintptr_t)base + g_srv_myweapons + slot * 4);
505}
506
478// Entity index of the player's active weapon from the authoritative server 507// Entity index of the player's active weapon from the authoritative server
479// entity. Used as a fallback if the client m_hActiveWeapon netvar is missing. 508// entity. Used as a fallback if the client m_hActiveWeapon netvar is missing.
480int server_active_weapon(int idx) { 509int server_active_weapon(int idx) {