summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/esp.cpp17
-rw-r--r--src/hl2h.h1
-rw-r--r--src/server.cpp56
3 files changed, 67 insertions, 7 deletions
diff --git a/src/esp.cpp b/src/esp.cpp
index b09588f..40e00f6 100644
--- a/src/esp.cpp
+++ b/src/esp.cpp
@@ -148,9 +148,18 @@ static bool model_is_neutral(const char *m) {
148 return false; 148 return false;
149} 149}
150 150
151static bool model_is_floor_turret(const char *m) {
152 return m && strstr(m, "floor_turret");
153}
154
151static bool npc_is_friendly(int idx, const char *model, int local_idx, 155static bool npc_is_friendly(int idx, const char *model, int local_idx,
152 bool &neutral) { 156 bool &neutral) {
153 int rel = server_npc_relation(idx, local_idx); 157 int rel = server_npc_relation(idx, local_idx);
158 int turret = -1;
159 // Citizen-built and Alyx-hacked turrets never add an m_Relationship row;
160 // their Classify() (CLASS_PLAYER_ALLY) comes from server runtime state.
161 if (model_is_floor_turret(model))
162 turret = server_npc_turret_flags(idx);
154 // Debug-only: relation values are the authoritative answer and are the 163 // Debug-only: relation values are the authoritative answer and are the
155 // first thing to check when a friendly NPC still looks hostile. Logged only 164 // first thing to check when a friendly NPC still looks hostile. Logged only
156 // when an entity's relation changes, so this does not flood the log. 165 // when an entity's relation changes, so this does not flood the log.
@@ -163,9 +172,9 @@ static bool npc_is_friendly(int idx, const char *model, int local_idx,
163 } 172 }
164 if (idx > 0 && idx < 2048 && rel_cache[idx] != rel) { 173 if (idx > 0 && idx < 2048 && rel_cache[idx] != rel) {
165 rel_cache[idx] = rel; 174 rel_cache[idx] = rel;
166 logd("npc %d (%s): relation=%d followtarget=%d local=%d", idx, 175 logd("npc %d (%s): relation=%d followtarget=%d turret=%d local=%d", idx,
167 model ? model : "?", rel, 176 model ? model : "?", rel,
168 (int)server_npc_has_follow_target(idx), local_idx); 177 (int)server_npc_has_follow_target(idx), turret, local_idx);
169 } 178 }
170 if (rel == 3) { // D_LI 179 if (rel == 3) { // D_LI
171 neutral = false; 180 neutral = false;
@@ -179,6 +188,10 @@ static bool npc_is_friendly(int idx, const char *model, int local_idx,
179 neutral = false; 188 neutral = false;
180 return false; 189 return false;
181 } 190 }
191 if (turret >= 0) {
192 neutral = false;
193 return turret != 0;
194 }
182 // Bugbait-controlled antlions follow a target even when their default 195 // Bugbait-controlled antlions follow a target even when their default
183 // relationship was never rewritten. 196 // relationship was never rewritten.
184 if (model && strstr(model, "antlion") && 197 if (model && strstr(model, "antlion") &&
diff --git a/src/hl2h.h b/src/hl2h.h
index 414e061..2824d3c 100644
--- a/src/hl2h.h
+++ b/src/hl2h.h
@@ -254,6 +254,7 @@ bool server_set_velocity_z(int idx, float vz);
254int server_active_weapon(int idx); 254int server_active_weapon(int idx);
255int server_npc_relation(int idx, int local_idx); 255int server_npc_relation(int idx, int local_idx);
256bool server_npc_has_follow_target(int idx); 256bool server_npc_has_follow_target(int idx);
257int server_npc_turret_flags(int idx);
257int server_model_index(void *base); 258int server_model_index(void *base);
258void find_modules(); 259void find_modules();
259void init_conmsg(); 260void init_conmsg();
diff --git a/src/server.cpp b/src/server.cpp
index 4702c44..19be144 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -27,11 +27,18 @@ enum { EDICT_UNKNOWN_OFF = 0x0C };
27// fieldOffset[1]@0xC, fieldSize@0x10, flags@0x12, externalName@0x14 27// fieldOffset[1]@0xC, fieldSize@0x10, flags@0x12, externalName@0x14
28enum { 28enum {
29 FIELD_INTEGER = 5, 29 FIELD_INTEGER = 5,
30 FIELD_BOOLEAN = 6,
30 FIELD_CHARACTER = 8, 31 FIELD_CHARACTER = 8,
31 FIELD_EMBEDDED = 10, 32 FIELD_EMBEDDED = 10,
33 FIELD_CUSTOM = 11,
32 FIELD_EHANDLE = 13 34 FIELD_EHANDLE = 13
33}; 35};
34 36
37// SF_FLOOR_TURRET_CITIZEN (npc_turret_floor.h). Citizen-built turrets
38// Classify() as CLASS_PLAYER_ALLY, i.e. they are friendly without ever adding
39// an entry to m_Relationship.
40static const int TURRET_FLAG_CITIZEN = 0x200;
41
35int g_srv_health = -1; 42int g_srv_health = -1;
36int g_srv_maxhealth = -1; 43int g_srv_maxhealth = -1;
37int g_srv_lifestate = -1; 44int g_srv_lifestate = -1;
@@ -41,6 +48,8 @@ int g_srv_velocity = -1;
41int g_srv_activeweapon = -1; 48int g_srv_activeweapon = -1;
42static int g_srv_relationship = -1; 49static int g_srv_relationship = -1;
43static int g_srv_followtarget = -1; 50static int g_srv_followtarget = -1;
51static int g_srv_spawnflags = -1;
52static int g_srv_hacked = -1;
44static bool g_srv_ready = false; 53static bool g_srv_ready = false;
45static bool g_srv_checked = false; 54static bool g_srv_checked = false;
46 55
@@ -58,7 +67,10 @@ static uintptr_t find_string(const char *s) {
58 67
59// Find the typedescription_t whose fieldName points at `name` and read its 68// Find the typedescription_t whose fieldName points at `name` and read its
60// fieldOffset. type filters false positives (m_iHealth is FIELD_INTEGER). 69// fieldOffset. type filters false positives (m_iHealth is FIELD_INTEGER).
61static int datamap_offset(const char *name, int want_type) { 70// max_off is generous on purpose: derived-class fields (m_bHackedByAlyx,
71// m_hFollowTarget) live well past the base class size.
72static int datamap_offset(const char *name, int want_type, int min_off = 0x40,
73 int max_off = 0x4000) {
62 uintptr_t str = find_string(name); 74 uintptr_t str = find_string(name);
63 if (!str) 75 if (!str)
64 return -1; 76 return -1;
@@ -75,7 +87,7 @@ static int datamap_offset(const char *name, int want_type) {
75 uintptr_t ext = *(const uintptr_t *)(e + 0x14); 87 uintptr_t ext = *(const uintptr_t *)(e + 0x14);
76 if (ftype != want_type) 88 if (ftype != want_type)
77 continue; 89 continue;
78 if (foff < 0x40 || foff > 0x2000) 90 if (foff < min_off || foff > max_off)
79 continue; 91 continue;
80 if (fsize < 1 || fsize > 64) 92 if (fsize < 1 || fsize > 64)
81 continue; 93 continue;
@@ -118,16 +130,24 @@ bool resolve_server_netvars() {
118 g_srv_absvelocity = datamap_offset("m_vecAbsVelocity", 3); // FIELD_VECTOR 130 g_srv_absvelocity = datamap_offset("m_vecAbsVelocity", 3); // FIELD_VECTOR
119 g_srv_velocity = datamap_offset("m_vecVelocity", 3); // FIELD_VECTOR 131 g_srv_velocity = datamap_offset("m_vecVelocity", 3); // FIELD_VECTOR
120 g_srv_activeweapon = datamap_offset("m_hActiveWeapon", FIELD_EHANDLE); 132 g_srv_activeweapon = datamap_offset("m_hActiveWeapon", FIELD_EHANDLE);
121 g_srv_relationship = datamap_offset("m_Relationship", FIELD_EMBEDDED); 133 // m_Relationship is DEFINE_UTLVECTOR, i.e. FIELD_CUSTOM (older builds used
134 // FIELD_EMBEDDED), never FIELD_EMBEDDED in the modern SDK.
135 g_srv_relationship = datamap_offset("m_Relationship", FIELD_CUSTOM);
136 if (g_srv_relationship < 0)
137 g_srv_relationship = datamap_offset("m_Relationship", FIELD_EMBEDDED);
122 g_srv_followtarget = datamap_offset("m_hFollowTarget", FIELD_EHANDLE); 138 g_srv_followtarget = datamap_offset("m_hFollowTarget", FIELD_EHANDLE);
139 // CBaseEntity::m_spawnflags is a DEFINE_KEYFIELD at a low offset.
140 g_srv_spawnflags = datamap_offset("m_spawnflags", FIELD_INTEGER, 0x4);
141 g_srv_hacked = datamap_offset("m_bHackedByAlyx", FIELD_BOOLEAN);
123 g_srv_ready = g_srv_health > 0; 142 g_srv_ready = g_srv_health > 0;
124 if (g_srv_ready) { 143 if (g_srv_ready) {
125 logf("server datamap (%s): health=%d maxhealth=%d lifeste=%d " 144 logf("server datamap (%s): health=%d maxhealth=%d lifeste=%d "
126 "modelindex=%d absvel=%d vel=%d activeweapon=%d relationship=%d " 145 "modelindex=%d absvel=%d vel=%d activeweapon=%d relationship=%d "
127 "followtarget=%d", 146 "followtarget=%d spawnflags=%d hacked=%d",
128 g_server.path.c_str(), g_srv_health, g_srv_maxhealth, g_srv_lifestate, 147 g_server.path.c_str(), g_srv_health, g_srv_maxhealth, g_srv_lifestate,
129 g_srv_modelindex, g_srv_absvelocity, g_srv_velocity, 148 g_srv_modelindex, g_srv_absvelocity, g_srv_velocity,
130 g_srv_activeweapon, g_srv_relationship, g_srv_followtarget); 149 g_srv_activeweapon, g_srv_relationship, g_srv_followtarget,
150 g_srv_spawnflags, g_srv_hacked);
131 fail_logged = false; 151 fail_logged = false;
132 } else if (!fail_logged) { 152 } else if (!fail_logged) {
133 fail_logged = true; 153 fail_logged = true;
@@ -245,6 +265,32 @@ bool server_npc_has_follow_target(int idx) {
245 return h != 0 && h != 0xFFFFFFFFu; 265 return h != 0 && h != 0xFFFFFFFFu;
246} 266}
247 267
268// Turret friendliness that never appears in m_Relationship: CNPC_FloorTurret's
269// Classify() reports CLASS_PLAYER_ALLY for citizen-built (SF_FLOOR_TURRET_CITIZEN)
270// and Alyx-hacked turrets, which makes the default class table D_LI toward the
271// player. Returns -1 if unknown, else bit0 = citizen, bit1 = hacked.
272int server_npc_turret_flags(int idx) {
273 if (!g_srv_ready || (g_srv_spawnflags < 0 && g_srv_hacked < 0))
274 return -1;
275 void *base = nullptr;
276 server_entity_health(idx, nullptr, nullptr, &base);
277 if (!base)
278 return -1;
279 uintptr_t b = (uintptr_t)base;
280 int flags = -1;
281 int hacked = -1;
282 if (g_srv_spawnflags > 0)
283 flags = *(int *)(b + g_srv_spawnflags);
284 if (g_srv_hacked > 0)
285 hacked = (int)*(unsigned char *)(b + g_srv_hacked);
286 int out = 0;
287 if (flags > 0 && (flags & TURRET_FLAG_CITIZEN))
288 out |= 1;
289 if (hacked > 0)
290 out |= 2;
291 return out;
292}
293
248bool server_set_velocity_z(int idx, float vz) { 294bool server_set_velocity_z(int idx, float vz) {
249 if (!g_srv_ready || !g_iface.engineserver || g_srv_absvelocity <= 0) 295 if (!g_srv_ready || !g_iface.engineserver || g_srv_absvelocity <= 0)
250 return false; 296 return false;