diff options
| author | salmonik <salmonik@tuta.io> | 2026-11-04 17:41:53 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-11-04 17:41:53 +0100 |
| commit | f4616119c64ddcaa14cf10727e7aba4a3e64910f (patch) | |
| tree | 2cb68ab8dfde040a6fd1baa0e77039fba32019a4 /src/server.cpp | |
| parent | Items/projectiles: ammo diagnostics, dispensers, launched RPG (diff) | |
| download | hl2h-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.
Diffstat (limited to '')
| -rw-r--r-- | src/server.cpp | 9 |
1 files changed, 6 insertions, 3 deletions
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). | ||
| 486 | int server_player_ammo(int idx, int type) { | 488 | int 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. | ||
| 497 | uint32_t server_player_weapon(int idx, int slot) { | 500 | uint32_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 |
