summaryrefslogtreecommitdiffstats
path: root/src/server.cpp
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-11-04 13:53:45 +0100
committersalmonik <salmonik@tuta.io>2026-11-04 13:53:45 +0100
commit12fde50efc7e1f1d123f9fc34ffef53d4387de2d (patch)
tree4de53807ce23df9b63598a0e3a12620ff19eb7cc /src/server.cpp
parentNo spread: detect pellet draws from the FireBullets seed call (diff)
downloadhl2h-12fde50efc7e1f1d123f9fc34ffef53d4387de2d.tar.gz
hl2h-12fde50efc7e1f1d123f9fc34ffef53d4387de2d.zip
Add ESP, aim, view and movement batch
- ESP: alert state (IDLE/ALERT/COMBAT/SCRIPT), enemy weapon labels, hostile/ neutral/friendly colour pickers, off-screen direction arrows, and trigger_changelevel volumes with next/previous map labels. - Aim: silent aim reuses the Aimbot FOV, vischeck and bone settings; add optional aim-punch compensation for aimbot, silent aim, triggerbot and melee (server bullets add m_vecPunchAngle to the usercmd angles). - View: always-on Camera / display card with camera FOV (writes the client m_iDefaultFOV netvar), viewmodel FOV and monitor gamma (client cvars). - Movement: Jump assist card with auto crouch-jump and edge jump (point ground probe 0.12 s ahead). - Spread: Engine spread override toggle gates the direct RandomFloat zeroing; off leaves only the seed search. - Server: entity count/base/classname helpers, trigger_changelevel map name and world bounds via IServerUnknown/ICollideable. - Netvars: m_vecPunchAngle and m_iDefaultFOV.
Diffstat (limited to '')
-rw-r--r--src/server.cpp96
1 files changed, 93 insertions, 3 deletions
diff --git a/src/server.cpp b/src/server.cpp
index f71b441..39bb162 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -52,6 +52,7 @@ static int g_srv_spawnflags = -1;
52static int g_srv_hacked = -1; 52static int g_srv_hacked = -1;
53static int g_srv_npcstate = -1; 53static int g_srv_npcstate = -1;
54static int g_srv_takedamage = -1; 54static int g_srv_takedamage = -1;
55static int g_srv_mapname = -1;
55static bool g_srv_ready = false; 56static bool g_srv_ready = false;
56static bool g_srv_checked = false; 57static bool g_srv_checked = false;
57 58
@@ -91,7 +92,7 @@ static int datamap_offset(const char *name, int want_type, int min_off = 0x40,
91 continue; 92 continue;
92 if (foff < min_off || foff > max_off) 93 if (foff < min_off || foff > max_off)
93 continue; 94 continue;
94 if (fsize < 1 || fsize > 64) 95 if (fsize < 1 || fsize > 128)
95 continue; 96 continue;
96 // DEFINE_KEYFIELD entries have an external name ("health"); plain 97 // DEFINE_KEYFIELD entries have an external name ("health"); plain
97 // DEFINE_FIELD entries have none. Either way it must be in server.so. 98 // DEFINE_FIELD entries have none. Either way it must be in server.so.
@@ -147,16 +148,19 @@ bool resolve_server_netvars() {
147 g_srv_takedamage = datamap_offset("m_takedamage", FIELD_CHARACTER); 148 g_srv_takedamage = datamap_offset("m_takedamage", FIELD_CHARACTER);
148 if (g_srv_takedamage < 0) 149 if (g_srv_takedamage < 0)
149 g_srv_takedamage = datamap_offset("m_takedamage", FIELD_INTEGER); 150 g_srv_takedamage = datamap_offset("m_takedamage", FIELD_INTEGER);
151 // CChangeLevel::m_szMapName is a char[cchMapNameMost] on trigger_changelevel.
152 g_srv_mapname = datamap_offset("m_szMapName", FIELD_CHARACTER, 0x40, 0x4000);
150 g_srv_ready = g_srv_health > 0; 153 g_srv_ready = g_srv_health > 0;
151 if (g_srv_ready) { 154 if (g_srv_ready) {
152 logf("server datamap (%s): health=%d maxhealth=%d lifeste=%d " 155 logf("server datamap (%s): health=%d maxhealth=%d lifeste=%d "
153 "modelindex=%d absvel=%d vel=%d activeweapon=%d relationship=%d " 156 "modelindex=%d absvel=%d vel=%d activeweapon=%d relationship=%d "
154 "followtarget=%d spawnflags=%d hacked=%d npcstate=%d " 157 "followtarget=%d spawnflags=%d hacked=%d npcstate=%d "
155 "takedamage=%d", 158 "takedamage=%d mapname=%d",
156 g_server.path.c_str(), g_srv_health, g_srv_maxhealth, g_srv_lifestate, 159 g_server.path.c_str(), g_srv_health, g_srv_maxhealth, g_srv_lifestate,
157 g_srv_modelindex, g_srv_absvelocity, g_srv_velocity, 160 g_srv_modelindex, g_srv_absvelocity, g_srv_velocity,
158 g_srv_activeweapon, g_srv_relationship, g_srv_followtarget, 161 g_srv_activeweapon, g_srv_relationship, g_srv_followtarget,
159 g_srv_spawnflags, g_srv_hacked, g_srv_npcstate, g_srv_takedamage); 162 g_srv_spawnflags, g_srv_hacked, g_srv_npcstate, g_srv_takedamage,
163 g_srv_mapname);
160 fail_logged = false; 164 fail_logged = false;
161 } else if (!fail_logged) { 165 } else if (!fail_logged) {
162 fail_logged = true; 166 fail_logged = true;
@@ -223,6 +227,92 @@ int server_model_index(void *base) {
223 return (int)*(short *)((uintptr_t)base + g_srv_modelindex); 227 return (int)*(short *)((uintptr_t)base + g_srv_modelindex);
224} 228}
225 229
230int server_entity_count() {
231 if (!g_iface.engineserver)
232 return 0;
233 using CountFn = int (*)(void *);
234 int n = vfunc<CountFn>(g_iface.engineserver, SRV_GetEntityCount)(
235 g_iface.engineserver);
236 return (n > 0 && n <= 8192) ? n : 0;
237}
238
239static void *edict_unknown(int idx) {
240 if (!g_iface.engineserver)
241 return nullptr;
242 using PEntityFn = void *(*)(void *, int);
243 void *edict = vfunc<PEntityFn>(g_iface.engineserver, SRV_PEntityOfEntIndex)(
244 g_iface.engineserver, idx);
245 if (!edict)
246 return nullptr;
247 return *(void **)((uintptr_t)edict + EDICT_UNKNOWN_OFF);
248}
249
250void *server_entity_base(int idx) {
251 void *unk = edict_unknown(idx);
252 if (!unk)
253 return nullptr;
254 using GetBaseFn = void *(*)(void *);
255 return vfunc<GetBaseFn>(unk, SRV_GetBaseEntity)(unk);
256}
257
258// Linked entity class name (e.g. "weapon_smg1", "trigger_changelevel").
259const char *server_entity_classname(int idx) {
260 void *unk = edict_unknown(idx);
261 if (!unk)
262 return nullptr;
263 using GetNetFn = void *(*)(void *);
264 void *net = vfunc<GetNetFn>(unk, 5)(unk);
265 if (!net)
266 return nullptr;
267 using NameFn = const char *(*)(void *);
268 return vfunc<NameFn>(net, 3)(net);
269}
270
271// A trigger_changelevel's target map and world-space brush bounds. The class
272// name comes from IServerNetworkable::GetClassName (slot 3); the volume from
273// ICollideable::WorldSpaceSurroundingBounds (slot 17, no virtual dtor).
274bool server_trigger_info(int idx, char *map, size_t mapn, Vector &mins,
275 Vector &maxs) {
276 if (!g_srv_ready || g_srv_mapname < 0)
277 return false;
278 void *unk = edict_unknown(idx);
279 if (!unk)
280 return false;
281 using GetNetFn = void *(*)(void *);
282 void *net = vfunc<GetNetFn>(unk, 5)(unk);
283 if (!net)
284 return false;
285 using NameFn = const char *(*)(void *);
286 const char *cls = vfunc<NameFn>(net, 3)(net);
287 if (!cls || !strcasestr(cls, "changelevel"))
288 return false;
289 void *base = server_entity_base(idx);
290 if (!base)
291 return false;
292 const char *m = (const char *)((uintptr_t)base + g_srv_mapname);
293 size_t n = strnlen(m, 32); // cchMapNameMost
294 if (n == 0 || n >= 32)
295 return false;
296 for (size_t i = 0; i < n; i++) {
297 unsigned char c = (unsigned char)m[i];
298 if (c < 0x20 || c > 0x7e)
299 return false;
300 }
301 if (map && mapn) {
302 if (n >= mapn)
303 n = mapn - 1;
304 memcpy(map, m, n);
305 map[n] = 0;
306 }
307 using GetCollFn = void *(*)(void *);
308 void *coll = vfunc<GetCollFn>(unk, 4)(unk);
309 if (coll) {
310 using BoundsFn = void (*)(void *, Vector *, Vector *);
311 vfunc<BoundsFn>(coll, 17)(coll, &mins, &maxs);
312 }
313 return true;
314}
315
226// Set the authoritative vertical velocity of a server entity. m_vecAbsVelocity 316// Set the authoritative vertical velocity of a server entity. m_vecAbsVelocity
227// is what CGameMovement::SetupMove reads through GetAbsVelocity(); writing the 317// is what CGameMovement::SetupMove reads through GetAbsVelocity(); writing the
228// networked m_vecVelocity directly is deliberately avoided because the server 318// networked m_vecVelocity directly is deliberately avoided because the server