summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-11-04 17:41:53 +0100
committersalmonik <salmonik@tuta.io>2026-11-04 17:41:53 +0100
commitf4616119c64ddcaa14cf10727e7aba4a3e64910f (patch)
tree2cb68ab8dfde040a6fd1baa0e77039fba32019a4
parentItems/projectiles: ammo diagnostics, dispensers, launched RPG (diff)
downloadhl2h-f4616119c64ddcaa14cf10727e7aba4a3e64910f.tar.gz
hl2h-f4616119c64ddcaa14cf10727e7aba4a3e64910f.zip
Items: skip the CNetworkArray length header for m_iAmmo/m_hMyWeapons
The datamap offset points at the array length header, so every ammo count was read one type early. That made the RPG slot read the buckshot count (30) and permanently exceed the 3-rocket cap, which is why RPG pickups never showed and grenades/AR2/weapon ammo never hid. Read the elements 4 bytes after the field offset.
-rw-r--r--CONTEXT.md6
-rw-r--r--src/server.cpp9
2 files changed, 12 insertions, 3 deletions
diff --git a/CONTEXT.md b/CONTEXT.md
index d1dbf26..92094bc 100644
--- a/CONTEXT.md
+++ b/CONTEXT.md
@@ -619,6 +619,12 @@ vocabulary / decisions.
619 `CBaseCombatCharacter::m_iAmmo` array) is compared with the `sk_max_*` cvar 619 `CBaseCombatCharacter::m_iAmmo` array) is compared with the `sk_max_*` cvar
620 (stock caps as fallback, e.g. RPG rockets = 3). Full ammo hides the pickup; 620 (stock caps as fallback, e.g. RPG rockets = 3). Full ammo hides the pickup;
621 if either value cannot be read the item stays visible. 621 if either value cannot be read the item stays visible.
622- The datamap offset for `m_iAmmo`/`m_hMyWeapons` points at the
623 `CNetworkArray` length header; the actual elements start **4 bytes later**.
624 Reading at the raw offset shifted every count by one type (e.g. RPG read the
625 buckshot count 30 against a max of 3, permanently hiding rockets).
626- Chargers and ammo/supply crates are dispensers and are exempt from the
627 fullness filter.
622- Scans the client entity list every 100 ms and tags health, ammo, armor/suit, 628- Scans the client entity list every 100 ms and tags health, ammo, armor/suit,
623 world weapon pickups, the two supply-crate models and generic items. 629 world weapon pickups, the two supply-crate models and generic items.
624- Each tag is a colored marker with a name and optional distance, filtered by 630- Each tag is a colored marker with a name and optional distance, filtered by
diff --git a/src/server.cpp b/src/server.cpp
index af0a8ff..09d198a 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -482,7 +482,9 @@ float server_grenade_radius(int idx) {
482 return *(float *)((uintptr_t)base + g_srv_grenade_radius); 482 return *(float *)((uintptr_t)base + g_srv_grenade_radius);
483} 483}
484 484
485// Server-side CBaseCombatCharacter::m_iAmmo array (reserve ammo). 485// Server-side CBaseCombatCharacter::m_iAmmo array (reserve ammo). The datamap
486// offset points at the CNetworkArray length header; the elements start 4 bytes
487// later (confirmed by the shifted current/max table).
486int server_player_ammo(int idx, int type) { 488int server_player_ammo(int idx, int type) {
487 if (!g_srv_ready || g_srv_ammo < 0 || type < 0 || type >= 32) 489 if (!g_srv_ready || g_srv_ammo < 0 || type < 0 || type >= 32)
488 return -1; 490 return -1;
@@ -490,10 +492,11 @@ int server_player_ammo(int idx, int type) {
490 server_entity_health(idx, nullptr, nullptr, &base); 492 server_entity_health(idx, nullptr, nullptr, &base);
491 if (!base) 493 if (!base)
492 return -1; 494 return -1;
493 return *(int *)((uintptr_t)base + g_srv_ammo + type * 4); 495 return *(int *)((uintptr_t)base + g_srv_ammo + 4 + type * 4);
494} 496}
495 497
496// Server-side m_hMyWeapons[48] handle for one inventory slot (0 if empty). 498// Server-side m_hMyWeapons[48] handle for one inventory slot (0 if empty).
499// Same 4-byte CNetworkArray header as m_iAmmo.
497uint32_t server_player_weapon(int idx, int slot) { 500uint32_t server_player_weapon(int idx, int slot) {
498 if (!g_srv_ready || g_srv_myweapons < 0 || slot < 0 || slot >= 48) 501 if (!g_srv_ready || g_srv_myweapons < 0 || slot < 0 || slot >= 48)
499 return 0; 502 return 0;
@@ -501,7 +504,7 @@ uint32_t server_player_weapon(int idx, int slot) {
501 server_entity_health(idx, nullptr, nullptr, &base); 504 server_entity_health(idx, nullptr, nullptr, &base);
502 if (!base) 505 if (!base)
503 return 0; 506 return 0;
504 return *(uint32_t *)((uintptr_t)base + g_srv_myweapons + slot * 4); 507 return *(uint32_t *)((uintptr_t)base + g_srv_myweapons + 4 + slot * 4);
505} 508}
506 509
507// Entity index of the player's active weapon from the authoritative server 510// Entity index of the player's active weapon from the authoritative server