summaryrefslogtreecommitdiffstats
path: root/src/server.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/server.cpp')
-rw-r--r--src/server.cpp66
1 files changed, 54 insertions, 12 deletions
diff --git a/src/server.cpp b/src/server.cpp
index 6479b25..f69ab30 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -58,7 +58,8 @@ static int g_srv_mapname = -1;
58static int g_srv_armor = -1; 58static int g_srv_armor = -1;
59static int g_srv_detonate = -1; 59static int g_srv_detonate = -1;
60static int g_srv_grenade_radius = -1; 60static int g_srv_grenade_radius = -1;
61static int g_srv_juice = -1; 61static int g_srv_juice_suit = -1;
62static int g_srv_juice_health = -1;
62static int g_srv_ammo = -1; 63static int g_srv_ammo = -1;
63static int g_srv_myweapons = -1; 64static int g_srv_myweapons = -1;
64static bool g_srv_ready = false; 65static bool g_srv_ready = false;
@@ -79,12 +80,17 @@ static uintptr_t find_string(const char *s) {
79// Find the typedescription_t whose fieldName points at `name` and read its 80// Find the typedescription_t whose fieldName points at `name` and read its
80// fieldOffset. type filters false positives (m_iHealth is FIELD_INTEGER). 81// fieldOffset. type filters false positives (m_iHealth is FIELD_INTEGER).
81// max_off is generous on purpose: derived-class fields (m_bHackedByAlyx, 82// max_off is generous on purpose: derived-class fields (m_bHackedByAlyx,
82// m_hFollowTarget) live well past the base class size. 83// m_hFollowTarget) live well past the base class size. A name can occur in
83static int datamap_offset(const char *name, int want_type, int min_off = 0x40, 84// several classes (m_iJuice is in both CWallHealth and CNewRecharge), so the
84 int max_off = 0x4000) { 85// collector returns every distinct offset.
86static int datamap_offsets(const char *name, int want_type, int min_off,
87 int max_off, int *out, int maxn) {
88 if (!out || maxn <= 0)
89 return 0;
85 uintptr_t str = find_string(name); 90 uintptr_t str = find_string(name);
86 if (!str) 91 if (!str)
87 return -1; 92 return 0;
93 int n = 0;
88 for (auto &r : g_server.ranges) { 94 for (auto &r : g_server.ranges) {
89 const uint8_t *base = (const uint8_t *)r.lo; 95 const uint8_t *base = (const uint8_t *)r.lo;
90 size_t span = r.hi - r.lo; 96 size_t span = r.hi - r.lo;
@@ -106,10 +112,22 @@ static int datamap_offset(const char *name, int want_type, int min_off = 0x40,
106 // DEFINE_FIELD entries have none. Either way it must be in server.so. 112 // DEFINE_FIELD entries have none. Either way it must be in server.so.
107 if (ext && !g_server.contains(ext)) 113 if (ext && !g_server.contains(ext))
108 continue; 114 continue;
109 return foff; 115 bool dup = false;
116 for (int i = 0; i < n; i++)
117 if (out[i] == foff)
118 dup = true;
119 if (!dup && n < maxn)
120 out[n++] = foff;
110 } 121 }
111 } 122 }
112 return -1; 123 return n;
124}
125
126static int datamap_offset(const char *name, int want_type, int min_off = 0x40,
127 int max_off = 0x4000) {
128 int out[1];
129 return datamap_offsets(name, want_type, min_off, max_off, out, 1) ? out[0]
130 : -1;
113} 131}
114 132
115bool resolve_server_netvars() { 133bool resolve_server_netvars() {
@@ -163,7 +181,22 @@ bool resolve_server_netvars() {
163 // CBaseGrenade fuse / blast radius (m_flDetonateTime is not networked). 181 // CBaseGrenade fuse / blast radius (m_flDetonateTime is not networked).
164 g_srv_detonate = datamap_offset("m_flDetonateTime", FIELD_TIME); 182 g_srv_detonate = datamap_offset("m_flDetonateTime", FIELD_TIME);
165 g_srv_grenade_radius = datamap_offset("m_DmgRadius", FIELD_FLOAT); 183 g_srv_grenade_radius = datamap_offset("m_DmgRadius", FIELD_FLOAT);
166 g_srv_juice = datamap_offset("m_iJuice", FIELD_INTEGER); 184 int juice_offs[8];
185 int njuice = datamap_offsets("m_iJuice", FIELD_INTEGER, 0x40, 0x4000,
186 juice_offs, 8);
187 int maxjuice = datamap_offset("m_iMaxJuice", FIELD_INTEGER);
188 // CNewRecharge (suit charger) has m_iMaxJuice five ints after m_iJuice;
189 // CWallHealth (health charger) has no m_iMaxJuice, so its offset is the
190 // other match.
191 g_srv_juice_suit = maxjuice > 20 ? maxjuice - 20 : -1;
192 g_srv_juice_health = -1;
193 for (int i = 0; i < njuice; i++)
194 if (juice_offs[i] != g_srv_juice_suit) {
195 g_srv_juice_health = juice_offs[i];
196 break;
197 }
198 if (g_srv_juice_health < 0 && njuice)
199 g_srv_juice_health = juice_offs[0];
167 // CBaseCombatCharacter arrays: ammo counts and the weapon handle list. 200 // CBaseCombatCharacter arrays: ammo counts and the weapon handle list.
168 g_srv_ammo = datamap_offset("m_iAmmo", FIELD_INTEGER, 0x40, 0x4000); 201 g_srv_ammo = datamap_offset("m_iAmmo", FIELD_INTEGER, 0x40, 0x4000);
169 g_srv_myweapons = datamap_offset("m_hMyWeapons", FIELD_EHANDLE); 202 g_srv_myweapons = datamap_offset("m_hMyWeapons", FIELD_EHANDLE);
@@ -173,13 +206,13 @@ bool resolve_server_netvars() {
173 "modelindex=%d absvel=%d vel=%d activeweapon=%d relationship=%d " 206 "modelindex=%d absvel=%d vel=%d activeweapon=%d relationship=%d "
174 "followtarget=%d spawnflags=%d hacked=%d npcstate=%d " 207 "followtarget=%d spawnflags=%d hacked=%d npcstate=%d "
175 "takedamage=%d mapname=%d armor=%d detonate=%d dmgradius=%d " 208 "takedamage=%d mapname=%d armor=%d detonate=%d dmgradius=%d "
176 "ammo=%d myweapons=%d juice=%d", 209 "ammo=%d myweapons=%d juice_suit=%d juice_health=%d",
177 g_server.path.c_str(), g_srv_health, g_srv_maxhealth, g_srv_lifestate, 210 g_server.path.c_str(), g_srv_health, g_srv_maxhealth, g_srv_lifestate,
178 g_srv_modelindex, g_srv_absvelocity, g_srv_velocity, 211 g_srv_modelindex, g_srv_absvelocity, g_srv_velocity,
179 g_srv_activeweapon, g_srv_relationship, g_srv_followtarget, 212 g_srv_activeweapon, g_srv_relationship, g_srv_followtarget,
180 g_srv_spawnflags, g_srv_hacked, g_srv_npcstate, g_srv_takedamage, 213 g_srv_spawnflags, g_srv_hacked, g_srv_npcstate, g_srv_takedamage,
181 g_srv_mapname, g_srv_armor, g_srv_detonate, g_srv_grenade_radius, 214 g_srv_mapname, g_srv_armor, g_srv_detonate, g_srv_grenade_radius,
182 g_srv_ammo, g_srv_myweapons, g_srv_juice); 215 g_srv_ammo, g_srv_myweapons, g_srv_juice_suit, g_srv_juice_health);
183 fail_logged = false; 216 fail_logged = false;
184 } else if (!fail_logged) { 217 } else if (!fail_logged) {
185 fail_logged = true; 218 fail_logged = true;
@@ -480,13 +513,22 @@ float server_grenade_radius(int idx) {
480// CWallHealth / CNewRecharge share the same m_iJuice layout (the fields before 513// CWallHealth / CNewRecharge share the same m_iJuice layout (the fields before
481// it match), so one datamap offset works for both health and suit chargers. 514// it match), so one datamap offset works for both health and suit chargers.
482int server_item_juice(int idx) { 515int server_item_juice(int idx) {
483 if (!g_srv_ready || g_srv_juice < 0) 516 if (!g_srv_ready)
517 return -1;
518 int off = g_srv_juice_health;
519 const char *cls = server_entity_classname(idx);
520 if (cls && (strstr(cls, "Recharge") || strstr(cls, "SuitCharger") ||
521 strstr(cls, "suitcharger")))
522 off = g_srv_juice_suit;
523 if (off < 0)
524 off = g_srv_juice_suit >= 0 ? g_srv_juice_suit : g_srv_juice_health;
525 if (off < 0)
484 return -1; 526 return -1;
485 void *base = nullptr; 527 void *base = nullptr;
486 server_entity_health(idx, nullptr, nullptr, &base); 528 server_entity_health(idx, nullptr, nullptr, &base);
487 if (!base) 529 if (!base)
488 return -1; 530 return -1;
489 return *(int *)((uintptr_t)base + g_srv_juice); 531 return *(int *)((uintptr_t)base + off);
490} 532}
491 533
492// Server-side CBaseCombatCharacter::m_iAmmo array (reserve ammo). The datamap 534// Server-side CBaseCombatCharacter::m_iAmmo array (reserve ammo). The datamap