diff options
| author | salmonik <salmonik@tuta.io> | 2026-10-28 12:12:33 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-10-28 12:12:33 +0100 |
| commit | a6f1e039ce988cf139c52d7853e96dc8496fb806 (patch) | |
| tree | 731021e0a401e08ddb8b1b5f4bc9adb589efc859 /src | |
| parent | Item ESP: fix AR2/RPG/stunstick world-model names (diff) | |
| download | hl2h-a6f1e039ce988cf139c52d7853e96dc8496fb806.tar.gz hl2h-a6f1e039ce988cf139c52d7853e96dc8496fb806.zip | |
Item ESP: recover CPhysicsProp model names from the server
Client CPhysicsProp entities frequently report m_nModelIndex == 0, so item
crates appeared as model=? and were never classified. Fall back to the
server entity's m_nModelIndex (set in CItem_ItemCrate::Spawn) when the client
model index is missing, then resolve the name through the shared modelinfo.
Diffstat (limited to 'src')
| -rw-r--r-- | src/esp.cpp | 22 |
1 files changed, 18 insertions, 4 deletions
diff --git a/src/esp.cpp b/src/esp.cpp index 2fb5d1b..ec3e94b 100644 --- a/src/esp.cpp +++ b/src/esp.cpp | |||
| @@ -100,10 +100,9 @@ static const char *entity_class_name(void *net, void **out_cc) { | |||
| 100 | 100 | ||
| 101 | // m_nModelIndex -> IVModelInfoClient(006)::GetModel/GetModelName. | 101 | // m_nModelIndex -> IVModelInfoClient(006)::GetModel/GetModelName. |
| 102 | // Itanium ABI: two dtor slots, so GetModel=2, GetModelName=4. | 102 | // Itanium ABI: two dtor slots, so GetModel=2, GetModelName=4. |
| 103 | static const char *entity_model_name(void *ent) { | 103 | static const char *model_name_from_index(int modelindex) { |
| 104 | if (!g_iface.modelinfo || g_net.modelindex < 0) return nullptr; | 104 | if (!g_iface.modelinfo || modelindex <= 0) |
| 105 | int modelindex = *(int *)((uintptr_t)ent + g_net.modelindex); | 105 | return nullptr; |
| 106 | if (modelindex <= 0) return nullptr; | ||
| 107 | const void *model = | 106 | const void *model = |
| 108 | vfunc<GetModelFn>(g_iface.modelinfo, 2)(g_iface.modelinfo, modelindex); | 107 | vfunc<GetModelFn>(g_iface.modelinfo, 2)(g_iface.modelinfo, modelindex); |
| 109 | if (!model) | 108 | if (!model) |
| @@ -111,6 +110,12 @@ static const char *entity_model_name(void *ent) { | |||
| 111 | return vfunc<GetModelNameFn>(g_iface.modelinfo, 4)(g_iface.modelinfo, model); | 110 | return vfunc<GetModelNameFn>(g_iface.modelinfo, 4)(g_iface.modelinfo, model); |
| 112 | } | 111 | } |
| 113 | 112 | ||
| 113 | static const char *entity_model_name(void *ent) { | ||
| 114 | if (!g_iface.modelinfo || g_net.modelindex < 0) return nullptr; | ||
| 115 | int modelindex = *(int *)((uintptr_t)ent + g_net.modelindex); | ||
| 116 | return model_name_from_index(modelindex); | ||
| 117 | } | ||
| 118 | |||
| 114 | // HL2 SP: generic NPCs (combine, metrocop, zombie, citizen, antlion, ...) all | 119 | // HL2 SP: generic NPCs (combine, metrocop, zombie, citizen, antlion, ...) all |
| 115 | // use the single client class "CAI_BaseNPC" — only special NPCs register their | 120 | // use the single client class "CAI_BaseNPC" — only special NPCs register their |
| 116 | // own class names (CNPC_Manhack, CNPC_Strider, CNPC_CombineGunship, ...). | 121 | // own class names (CNPC_Manhack, CNPC_Strider, CNPC_CombineGunship, ...). |
| @@ -1629,6 +1634,7 @@ static void items_collect(int local_idx) { | |||
| 1629 | g_nitems = 0; | 1634 | g_nitems = 0; |
| 1630 | if (!g_iface.entitylist || g_net.origin < 0 || g_net.modelindex < 0) | 1635 | if (!g_iface.entitylist || g_net.origin < 0 || g_net.modelindex < 0) |
| 1631 | return; | 1636 | return; |
| 1637 | resolve_server_netvars(); | ||
| 1632 | 1638 | ||
| 1633 | using Fn = int (*)(void *); | 1639 | using Fn = int (*)(void *); |
| 1634 | int hi = vfunc<Fn>(g_iface.entitylist, 6)(g_iface.entitylist); | 1640 | int hi = vfunc<Fn>(g_iface.entitylist, 6)(g_iface.entitylist); |
| @@ -1664,6 +1670,14 @@ static void items_collect(int local_idx) { | |||
| 1664 | continue; | 1670 | continue; |
| 1665 | const char *cls = entity_class_name(net, nullptr); | 1671 | const char *cls = entity_class_name(net, nullptr); |
| 1666 | const char *model = entity_model_name(ent); | 1672 | const char *model = entity_model_name(ent); |
| 1673 | // CPhysicsProp's client m_nModelIndex is often 0 (e.g. item_item_crate); | ||
| 1674 | // fall back to the server entity's model index, which is set in Spawn(). | ||
| 1675 | if ((!model || !*model) && g_iface.engineserver) { | ||
| 1676 | void *sbase = nullptr; | ||
| 1677 | server_entity_health(idx, nullptr, nullptr, &sbase); | ||
| 1678 | if (sbase) | ||
| 1679 | model = model_name_from_index(server_model_index(sbase)); | ||
| 1680 | } | ||
| 1667 | char label[128]; | 1681 | char label[128]; |
| 1668 | int kind = classify_item(model, cls, label, sizeof(label)); | 1682 | int kind = classify_item(model, cls, label, sizeof(label)); |
| 1669 | 1683 | ||
