diff options
Diffstat (limited to '')
| -rw-r--r-- | src/hl2h.cpp | 82 | ||||
| -rw-r--r-- | src/hl2h.h | 3 | ||||
| -rw-r--r-- | src/menu.cpp | 17 | ||||
| -rw-r--r-- | src/netvars.cpp | 67 | ||||
| -rw-r--r-- | src/server.cpp | 23 |
5 files changed, 174 insertions, 18 deletions
diff --git a/src/hl2h.cpp b/src/hl2h.cpp index 125d94e..4ba30bc 100644 --- a/src/hl2h.cpp +++ b/src/hl2h.cpp | |||
| @@ -186,6 +186,8 @@ static void apply_autostrafe(CUserCmd *cmd, const float *vel) { | |||
| 186 | static constexpr float JB_SAFE_FALL = 526.5f; | 186 | static constexpr float JB_SAFE_FALL = 526.5f; |
| 187 | static constexpr float JB_GRAVITY = 600.0f; | 187 | static constexpr float JB_GRAVITY = 600.0f; |
| 188 | static constexpr float JB_DUCK_RAISE = 36.0f; | 188 | static constexpr float JB_DUCK_RAISE = 36.0f; |
| 189 | static constexpr float JB_TARGET_DIST = 37.0f; // middle of the unduck window | ||
| 190 | static constexpr float JB_TICK = 0.015f; // Source interval_per_tick | ||
| 189 | 191 | ||
| 190 | static int jb_state = 0; // 0 idle, 1 ducked/waiting for the unduck window | 192 | static int jb_state = 0; // 0 idle, 1 ducked/waiting for the unduck window |
| 191 | static int jb_local = -1; | 193 | static int jb_local = -1; |
| @@ -213,6 +215,30 @@ static void jumpbug_reset() { | |||
| 213 | jb_tracked = false; | 215 | jb_tracked = false; |
| 214 | } | 216 | } |
| 215 | 217 | ||
| 218 | // Choose a vertical velocity so this command's movement ends with the feet | ||
| 219 | // `target` units above the floor. The engine does StartGravity (half a tick), | ||
| 220 | // moves by velocity*dt, then FinishGravity; displacement is | ||
| 221 | // (vz - 0.5*g*dt)*dt. dist is the distance before the command's movement. | ||
| 222 | static bool jb_force_next_distance(int local, void *ent, const float *vel, | ||
| 223 | float dist, float target) { | ||
| 224 | if (g_net.absvelocity < 0 || g_net.velocity < 0) | ||
| 225 | return false; | ||
| 226 | float dz = target - dist; | ||
| 227 | float vz = dz / JB_TICK + 0.5f * JB_GRAVITY * JB_TICK; | ||
| 228 | if (vz < -2000.0f) | ||
| 229 | vz = -2000.0f; | ||
| 230 | if (vz > 2000.0f) | ||
| 231 | vz = 2000.0f; | ||
| 232 | if (!server_set_velocity_z(local, vz)) | ||
| 233 | return false; | ||
| 234 | // Keep the local prediction in step with the authoritative server entity: | ||
| 235 | // C_BaseEntity::GetAbsVelocity() reads the non-networked m_vecAbsVelocity, | ||
| 236 | // while the networked m_vecVelocity is what the rest of the client sees. | ||
| 237 | *(float *)((uintptr_t)ent + g_net.absvelocity + 8) = vz; | ||
| 238 | ((float *)vel)[2] = vz; | ||
| 239 | return true; | ||
| 240 | } | ||
| 241 | |||
| 216 | static bool jumpbug_apply(CUserCmd *cmd, int local, void *ent, int flags, | 242 | static bool jumpbug_apply(CUserCmd *cmd, int local, void *ent, int flags, |
| 217 | const float *vel, float frametime) { | 243 | const float *vel, float frametime) { |
| 218 | if (!feature_active(FEAT_JUMPBUG) || !g_iface.engine || | 244 | if (!feature_active(FEAT_JUMPBUG) || !g_iface.engine || |
| @@ -285,6 +311,10 @@ static bool jumpbug_apply(CUserCmd *cmd, int local, void *ent, int flags, | |||
| 285 | logf("jumpbug: tracking x=%.1f vz=%.1f impact=%.1f", x, vz, impact); | 311 | logf("jumpbug: tracking x=%.1f vz=%.1f impact=%.1f", x, vz, impact); |
| 286 | } | 312 | } |
| 287 | 313 | ||
| 314 | bool assist = jumpbug_assist_enabled(); | ||
| 315 | if (assist) | ||
| 316 | resolve_server_netvars(); | ||
| 317 | |||
| 288 | if (jb_drop <= 0.05f) { | 318 | if (jb_drop <= 0.05f) { |
| 289 | // No measured fall yet. Bias toward the default 15 ms Source tick when | 319 | // No measured fall yet. Bias toward the default 15 ms Source tick when |
| 290 | // the input frametime is smaller: setting the duck up early leaves us | 320 | // the input frametime is smaller: setting the duck up early leaves us |
| @@ -310,29 +340,59 @@ static bool jumpbug_apply(CUserCmd *cmd, int local, void *ent, int flags, | |||
| 310 | trigger = true; | 340 | trigger = true; |
| 311 | jb_state = 0; | 341 | jb_state = 0; |
| 312 | jb_tracked = false; | 342 | jb_tracked = false; |
| 313 | if (jb_logs < 20) { | 343 | if (jb_logs < 30) { |
| 314 | jb_logs++; | 344 | jb_logs++; |
| 315 | logf("jumpbug: trigger x=%.2f drop=%.2f vz=%.1f", x, jb_drop, vz); | 345 | logf("jumpbug: trigger x=%.2f drop=%.2f vz=%.1f assist=%d", x, jb_drop, |
| 346 | vz, (int)assist); | ||
| 316 | } | 347 | } |
| 317 | } else { | 348 | } else { |
| 318 | // Hold the crouch and wait for the window. Staying ducked is required | 349 | if (assist) { |
| 319 | // for the unduck trick; releasing early would just add a phase shift | 350 | float next_x = x - jb_drop; |
| 320 | // and fight a user who is holding crouch. | 351 | if (next_x <= 38.0f && next_x < 36.0f) { |
| 352 | // The natural next command would skip the 2-unit window. Force the | ||
| 353 | // post-command distance to 37 so the next command can unduck+jump. | ||
| 354 | if (jb_force_next_distance(local, ent, vel, x, JB_TARGET_DIST)) { | ||
| 355 | if (jb_logs < 30) { | ||
| 356 | jb_logs++; | ||
| 357 | logf("jumpbug: assist duck x=%.2f next=%.2f -> vz=%.1f", x, | ||
| 358 | next_x, vel[2]); | ||
| 359 | } | ||
| 360 | } else { | ||
| 361 | static bool warned = false; | ||
| 362 | if (!warned) { | ||
| 363 | warned = true; | ||
| 364 | logf("jumpbug: assist unavailable (server velocity failed)"); | ||
| 365 | } | ||
| 366 | } | ||
| 367 | } | ||
| 368 | } | ||
| 369 | // Hold the crouch and wait for the window (or for the assisted | ||
| 370 | // position on the next command). | ||
| 321 | cmd->buttons |= IN_DUCK; | 371 | cmd->buttons |= IN_DUCK; |
| 322 | jb_state = 1; | 372 | jb_state = 1; |
| 323 | } | 373 | } |
| 324 | } else { | 374 | } else { |
| 325 | if (jb_state == 1) | 375 | if (jb_state == 1) |
| 326 | jb_state = 0; // expected to be ducked but is not | 376 | jb_state = 0; // expected to be ducked but is not |
| 327 | if (x <= jb_drop + 2.0f) { | 377 | float setup_limit = jb_drop + (assist ? 4.0f : 2.0f); |
| 328 | // Setup frame: raise the feet 36 units here so the next frame starts | 378 | if (x <= setup_limit) { |
| 329 | // near the middle of the unduck window. | 379 | // Setup frame: raise the feet 36 units here so the next command starts |
| 380 | // near the middle of the unduck window. With assist enabled, also pin | ||
| 381 | // the post-command distance to exactly 37. | ||
| 382 | bool forced = false; | ||
| 383 | if (assist) | ||
| 384 | forced = jb_force_next_distance(local, ent, vel, x + JB_DUCK_RAISE, | ||
| 385 | JB_TARGET_DIST); | ||
| 330 | cmd->buttons |= IN_DUCK; | 386 | cmd->buttons |= IN_DUCK; |
| 331 | jb_state = 1; | 387 | jb_state = 1; |
| 332 | if (jb_logs < 20) { | 388 | if (jb_logs < 30) { |
| 333 | jb_logs++; | 389 | jb_logs++; |
| 334 | logf("jumpbug: setup x=%.2f drop=%.2f pred=%.2f vz=%.1f", x, jb_drop, | 390 | if (assist) |
| 335 | x + JB_DUCK_RAISE - jb_drop, vz); | 391 | logf("jumpbug: setup assist x=%.2f drop=%.2f vz=%.1f ok=%d", x, |
| 392 | jb_drop, vel[2], (int)forced); | ||
| 393 | else | ||
| 394 | logf("jumpbug: setup x=%.2f drop=%.2f pred=%.2f vz=%.1f", x, | ||
| 395 | jb_drop, x + JB_DUCK_RAISE - jb_drop, vz); | ||
| 336 | } | 396 | } |
| 337 | } | 397 | } |
| 338 | } | 398 | } |
| @@ -81,6 +81,7 @@ struct NetVars { | |||
| 81 | int maxs = -1; | 81 | int maxs = -1; |
| 82 | int lifestate = -1; | 82 | int lifestate = -1; |
| 83 | int modelindex = -1; | 83 | int modelindex = -1; |
| 84 | int absvelocity = -1; // client C_BaseEntity::m_vecAbsVelocity (prediction) | ||
| 84 | // ponytail: mins/maxs may be absent from some HL2 netvar tables; esp.cpp falls back to h=72 | 85 | // ponytail: mins/maxs may be absent from some HL2 netvar tables; esp.cpp falls back to h=72 |
| 85 | bool ok() const { return flags >= 0 && velocity >= 0 && movetype >= 0; } | 86 | bool ok() const { return flags >= 0 && velocity >= 0 && movetype >= 0; } |
| 86 | // ponytail: m_iMaxHealth isn't a netvar in HL2 SP; esp.cpp falls back to 100. | 87 | // ponytail: m_iMaxHealth isn't a netvar in HL2 SP; esp.cpp falls back to 100. |
| @@ -177,6 +178,7 @@ bool find_input_global(); | |||
| 177 | void resolve_netvars(); | 178 | void resolve_netvars(); |
| 178 | bool resolve_server_netvars(); | 179 | bool resolve_server_netvars(); |
| 179 | int server_entity_health(int idx, int *maxhealth, int *lifestate, void **base_out); | 180 | int server_entity_health(int idx, int *maxhealth, int *lifestate, void **base_out); |
| 181 | bool server_set_velocity_z(int idx, float vz); | ||
| 180 | int server_model_index(void *base); | 182 | int server_model_index(void *base); |
| 181 | void find_modules(); | 183 | void find_modules(); |
| 182 | void init_conmsg(); | 184 | void init_conmsg(); |
| @@ -185,6 +187,7 @@ bool feature_has_bind(int feature); | |||
| 185 | bool feature_bind_held(int feature); | 187 | bool feature_bind_held(int feature); |
| 186 | bool esp_option(int opt); | 188 | bool esp_option(int opt); |
| 187 | bool bhop_autostrafe_enabled(); | 189 | bool bhop_autostrafe_enabled(); |
| 190 | bool jumpbug_assist_enabled(); | ||
| 188 | unsigned aimbot_bone_mask(); | 191 | unsigned aimbot_bone_mask(); |
| 189 | int aimbot_smooth_step(); | 192 | int aimbot_smooth_step(); |
| 190 | bool aimbot_vischeck_enabled(); | 193 | bool aimbot_vischeck_enabled(); |
diff --git a/src/menu.cpp b/src/menu.cpp index 0ed89d4..4f76529 100644 --- a/src/menu.cpp +++ b/src/menu.cpp | |||
| @@ -63,6 +63,7 @@ static bool g_bhop_autostrafe = true; | |||
| 63 | 63 | ||
| 64 | static FeatureState g_abh{true, 0}; | 64 | static FeatureState g_abh{true, 0}; |
| 65 | static FeatureState g_jumpbug{true, 0}; | 65 | static FeatureState g_jumpbug{true, 0}; |
| 66 | static bool g_jumpbug_assist = false; | ||
| 66 | static FeatureState g_esp{true, 0}; | 67 | static FeatureState g_esp{true, 0}; |
| 67 | static FeatureState g_aimbot{true, 604}; // default Mouse5, rebindable in menu | 68 | static FeatureState g_aimbot{true, 604}; // default Mouse5, rebindable in menu |
| 68 | static FeatureState g_triggerbot{true, 603}; // default Mouse4 | 69 | static FeatureState g_triggerbot{true, 603}; // default Mouse4 |
| @@ -99,6 +100,7 @@ static MenuNode g_abh_feat{NODE_FEATURE, "ABH", false, 1, {}, nullptr, nullptr, | |||
| 99 | static MenuNode g_abh_active{NODE_TOGGLE, "Active", false, 0, {}, &g_abh.active, nullptr, nullptr}; | 100 | static MenuNode g_abh_active{NODE_TOGGLE, "Active", false, 0, {}, &g_abh.active, nullptr, nullptr}; |
| 100 | static MenuNode g_jumpbug_feat{NODE_FEATURE, "Jumpbug", false, 1, {}, nullptr, nullptr, nullptr}; | 101 | static MenuNode g_jumpbug_feat{NODE_FEATURE, "Jumpbug", false, 1, {}, nullptr, nullptr, nullptr}; |
| 101 | static MenuNode g_jumpbug_active{NODE_TOGGLE, "Active", false, 0, {}, &g_jumpbug.active, nullptr, nullptr}; | 102 | static MenuNode g_jumpbug_active{NODE_TOGGLE, "Active", false, 0, {}, &g_jumpbug.active, nullptr, nullptr}; |
| 103 | static MenuNode g_jumpbug_assist_node{NODE_TOGGLE, "Assist timing", false, 0, {}, &g_jumpbug_assist, nullptr, nullptr}; | ||
| 102 | static MenuNode g_esp_feat{NODE_FEATURE, "ESP", false, 1, {}, nullptr, nullptr, nullptr}; | 104 | static MenuNode g_esp_feat{NODE_FEATURE, "ESP", false, 1, {}, nullptr, nullptr, nullptr}; |
| 103 | static MenuNode g_esp_active{NODE_TOGGLE, "Active", false, 0, {}, &g_esp.active, nullptr, nullptr}; | 105 | static MenuNode g_esp_active{NODE_TOGGLE, "Active", false, 0, {}, &g_esp.active, nullptr, nullptr}; |
| 104 | static MenuNode g_esp_opt_nodes[ESP_OPT_COUNT] = { | 106 | static MenuNode g_esp_opt_nodes[ESP_OPT_COUNT] = { |
| @@ -150,7 +152,8 @@ static void build_tree() { | |||
| 150 | g_abh_feat.children[0] = &g_abh_active; | 152 | g_abh_feat.children[0] = &g_abh_active; |
| 151 | g_abh_feat.child_count = 1; | 153 | g_abh_feat.child_count = 1; |
| 152 | g_jumpbug_feat.children[0] = &g_jumpbug_active; | 154 | g_jumpbug_feat.children[0] = &g_jumpbug_active; |
| 153 | g_jumpbug_feat.child_count = 1; | 155 | g_jumpbug_feat.children[1] = &g_jumpbug_assist_node; |
| 156 | g_jumpbug_feat.child_count = 2; | ||
| 154 | g_esp_feat.children[0] = &g_esp_active; | 157 | g_esp_feat.children[0] = &g_esp_active; |
| 155 | g_aimbot_feat.children[0] = &g_aimbot_active; | 158 | g_aimbot_feat.children[0] = &g_aimbot_active; |
| 156 | g_aimbot_feat.children[1] = &g_aimbot_bind; | 159 | g_aimbot_feat.children[1] = &g_aimbot_bind; |
| @@ -302,7 +305,8 @@ static void config_save() { | |||
| 302 | fprintf(f, "[bhop]\nactive=%d\nbind=%d\nautostrafe=%d\n", g_bhop.active, | 305 | fprintf(f, "[bhop]\nactive=%d\nbind=%d\nautostrafe=%d\n", g_bhop.active, |
| 303 | g_bhop.bind, g_bhop_autostrafe); | 306 | g_bhop.bind, g_bhop_autostrafe); |
| 304 | fprintf(f, "[abh]\nactive=%d\nbind=%d\n", g_abh.active, g_abh.bind); | 307 | fprintf(f, "[abh]\nactive=%d\nbind=%d\n", g_abh.active, g_abh.bind); |
| 305 | fprintf(f, "[jumpbug]\nactive=%d\n", g_jumpbug.active); | 308 | fprintf(f, "[jumpbug]\nactive=%d\nassist=%d\n", g_jumpbug.active, |
| 309 | (int)g_jumpbug_assist); | ||
| 306 | fprintf(f, "[aimbot]\nactive=%d\nbind=%d\nsmooth=%d\nvischeck=%d\n" | 310 | fprintf(f, "[aimbot]\nactive=%d\nbind=%d\nsmooth=%d\nvischeck=%d\n" |
| 307 | "fov=%d\nfov_circle=%d\n" | 311 | "fov=%d\nfov_circle=%d\n" |
| 308 | "bone_head=%d\nbone_torso=%d\nbone_larm=%d\nbone_rarm=%d\n" | 312 | "bone_head=%d\nbone_torso=%d\nbone_larm=%d\nbone_rarm=%d\n" |
| @@ -332,7 +336,8 @@ static void config_load() { | |||
| 332 | return; | 336 | return; |
| 333 | char line[256]; | 337 | char line[256]; |
| 334 | FeatureState *cur = nullptr; | 338 | FeatureState *cur = nullptr; |
| 335 | bool in_esp = false, in_bhop = false, in_aim = false, in_silent = false; | 339 | bool in_esp = false, in_bhop = false, in_aim = false, in_silent = false, |
| 340 | in_jumpbug = false; | ||
| 336 | while (fgets(line, sizeof(line), f)) { | 341 | while (fgets(line, sizeof(line), f)) { |
| 337 | line[strcspn(line, "\r\n")] = 0; | 342 | line[strcspn(line, "\r\n")] = 0; |
| 338 | if (line[0] == '[') { | 343 | if (line[0] == '[') { |
| @@ -356,6 +361,7 @@ static void config_load() { | |||
| 356 | in_bhop = (cur == &g_bhop); | 361 | in_bhop = (cur == &g_bhop); |
| 357 | in_aim = (cur == &g_aimbot); | 362 | in_aim = (cur == &g_aimbot); |
| 358 | in_silent = (cur == &g_silentaim); | 363 | in_silent = (cur == &g_silentaim); |
| 364 | in_jumpbug = (cur == &g_jumpbug); | ||
| 359 | } else if (cur) { | 365 | } else if (cur) { |
| 360 | char *eq = strchr(line, '='); | 366 | char *eq = strchr(line, '='); |
| 361 | if (!eq) | 367 | if (!eq) |
| @@ -365,6 +371,9 @@ static void config_load() { | |||
| 365 | if (in_bhop && strcmp(key, "autostrafe") == 0) { | 371 | if (in_bhop && strcmp(key, "autostrafe") == 0) { |
| 366 | g_bhop_autostrafe = atoi(val) != 0; | 372 | g_bhop_autostrafe = atoi(val) != 0; |
| 367 | } | 373 | } |
| 374 | if (in_jumpbug && strcmp(key, "assist") == 0) { | ||
| 375 | g_jumpbug_assist = atoi(val) != 0; | ||
| 376 | } | ||
| 368 | if (in_aim) { | 377 | if (in_aim) { |
| 369 | if (strcmp(key, "smooth") == 0) { | 378 | if (strcmp(key, "smooth") == 0) { |
| 370 | int v = atoi(val); | 379 | int v = atoi(val); |
| @@ -557,6 +566,8 @@ bool esp_option(int opt) { | |||
| 557 | 566 | ||
| 558 | bool bhop_autostrafe_enabled() { return g_bhop_autostrafe; } | 567 | bool bhop_autostrafe_enabled() { return g_bhop_autostrafe; } |
| 559 | 568 | ||
| 569 | bool jumpbug_assist_enabled() { return g_jumpbug_assist; } | ||
| 570 | |||
| 560 | unsigned aimbot_bone_mask() { | 571 | unsigned aimbot_bone_mask() { |
| 561 | unsigned m = 0; | 572 | unsigned m = 0; |
| 562 | if (g_aim_bones[AIM_BONE_HEAD]) | 573 | if (g_aim_bones[AIM_BONE_HEAD]) |
diff --git a/src/netvars.cpp b/src/netvars.cpp index b4e31d5..98ebe31 100644 --- a/src/netvars.cpp +++ b/src/netvars.cpp | |||
| @@ -185,6 +185,66 @@ static bool resolve_movetype() { | |||
| 185 | return true; | 185 | return true; |
| 186 | } | 186 | } |
| 187 | 187 | ||
| 188 | static bool resolve_absvelocity() { | ||
| 189 | static const char needle[] = "m_vecAbsVelocity"; | ||
| 190 | const size_t nlen = sizeof(needle); | ||
| 191 | |||
| 192 | uintptr_t str_va = 0; | ||
| 193 | for (auto &r : g_client.ranges) { | ||
| 194 | const uint8_t *base = (const uint8_t *)r.lo; | ||
| 195 | size_t span = r.hi - r.lo; | ||
| 196 | for (size_t i = 0; i + nlen <= span; i++) { | ||
| 197 | if (memcmp(base + i, needle, nlen) == 0) { | ||
| 198 | str_va = r.lo + i; | ||
| 199 | break; | ||
| 200 | } | ||
| 201 | } | ||
| 202 | if (str_va) | ||
| 203 | break; | ||
| 204 | } | ||
| 205 | if (!str_va) { | ||
| 206 | logf("m_vecAbsVelocity string not found"); | ||
| 207 | return false; | ||
| 208 | } | ||
| 209 | |||
| 210 | uint32_t offs[16]; | ||
| 211 | int counts[16] = {}; | ||
| 212 | int ncand = 0; | ||
| 213 | const uint32_t target = (uint32_t)str_va; | ||
| 214 | for (auto &r : g_client.ranges) { | ||
| 215 | const uint8_t *base = (const uint8_t *)r.lo; | ||
| 216 | size_t span = r.hi - r.lo; | ||
| 217 | for (size_t off = 4; off + 8 <= span; off += 4) { | ||
| 218 | if (*(const uint32_t *)(base + off) != target) | ||
| 219 | continue; | ||
| 220 | uint32_t ftype = *(const uint32_t *)(base + off - 4); | ||
| 221 | int32_t foff = *(const int32_t *)(base + off + 4); | ||
| 222 | if (ftype != 3 || foff < 0x40 || foff > 0x400) | ||
| 223 | continue; | ||
| 224 | int k; | ||
| 225 | for (k = 0; k < ncand; k++) | ||
| 226 | if (offs[k] == (uint32_t)foff) | ||
| 227 | break; | ||
| 228 | if (k == ncand && ncand < 16) { | ||
| 229 | offs[ncand] = (uint32_t)foff; | ||
| 230 | ncand++; | ||
| 231 | } | ||
| 232 | if (k < ncand) | ||
| 233 | counts[k]++; | ||
| 234 | } | ||
| 235 | } | ||
| 236 | if (ncand == 0) { | ||
| 237 | logf("m_vecAbsVelocity typedescription not found"); | ||
| 238 | return false; | ||
| 239 | } | ||
| 240 | int best = 0; | ||
| 241 | for (int k = 1; k < ncand; k++) | ||
| 242 | if (counts[k] > counts[best]) | ||
| 243 | best = k; | ||
| 244 | g_net.absvelocity = (int)offs[best]; | ||
| 245 | return true; | ||
| 246 | } | ||
| 247 | |||
| 188 | void resolve_netvars() { | 248 | void resolve_netvars() { |
| 189 | using GetAllClassesFn = void *(*)(void *); | 249 | using GetAllClassesFn = void *(*)(void *); |
| 190 | uintptr_t head = | 250 | uintptr_t head = |
| @@ -205,10 +265,13 @@ void resolve_netvars() { | |||
| 205 | // movetype unresolved — bhop still works, just no noclip/ladder guard | 265 | // movetype unresolved — bhop still works, just no noclip/ladder guard |
| 206 | logf("movetype guard disabled"); | 266 | logf("movetype guard disabled"); |
| 207 | } | 267 | } |
| 268 | if (g_net.absvelocity < 0 && !resolve_absvelocity()) | ||
| 269 | logf("m_vecAbsVelocity unresolved; jumpbug assist disabled"); | ||
| 208 | logf("netvars: flags=%d velocity=%d movetype=%d health=%d maxhealth=%d " | 270 | logf("netvars: flags=%d velocity=%d movetype=%d health=%d maxhealth=%d " |
| 209 | "teamnum=%d origin=%d viewoffset=%d mins=%d maxs=%d lifeste=%d " | 271 | "teamnum=%d origin=%d viewoffset=%d mins=%d maxs=%d lifeste=%d " |
| 210 | "modelindex=%d", | 272 | "modelindex=%d absvelocity=%d", |
| 211 | g_net.flags, g_net.velocity, g_net.movetype, g_net.health, | 273 | g_net.flags, g_net.velocity, g_net.movetype, g_net.health, |
| 212 | g_net.maxhealth, g_net.teamnum, g_net.origin, g_net.viewoffset, | 274 | g_net.maxhealth, g_net.teamnum, g_net.origin, g_net.viewoffset, |
| 213 | g_net.mins, g_net.maxs, g_net.lifestate, g_net.modelindex); | 275 | g_net.mins, g_net.maxs, g_net.lifestate, g_net.modelindex, |
| 276 | g_net.absvelocity); | ||
| 214 | } | 277 | } |
diff --git a/src/server.cpp b/src/server.cpp index 6feb55b..3766906 100644 --- a/src/server.cpp +++ b/src/server.cpp | |||
| @@ -31,6 +31,8 @@ int g_srv_health = -1; | |||
| 31 | int g_srv_maxhealth = -1; | 31 | int g_srv_maxhealth = -1; |
| 32 | int g_srv_lifestate = -1; | 32 | int g_srv_lifestate = -1; |
| 33 | int g_srv_modelindex = -1; | 33 | int g_srv_modelindex = -1; |
| 34 | int g_srv_absvelocity = -1; | ||
| 35 | int g_srv_velocity = -1; | ||
| 34 | static bool g_srv_ready = false; | 36 | static bool g_srv_ready = false; |
| 35 | static bool g_srv_checked = false; | 37 | static bool g_srv_checked = false; |
| 36 | 38 | ||
| @@ -105,12 +107,14 @@ bool resolve_server_netvars() { | |||
| 105 | g_srv_maxhealth = datamap_offset("m_iMaxHealth", FIELD_INTEGER); | 107 | g_srv_maxhealth = datamap_offset("m_iMaxHealth", FIELD_INTEGER); |
| 106 | g_srv_lifestate = datamap_offset("m_lifeState", FIELD_CHARACTER); | 108 | g_srv_lifestate = datamap_offset("m_lifeState", FIELD_CHARACTER); |
| 107 | g_srv_modelindex = datamap_offset("m_nModelIndex", 7); // FIELD_SHORT | 109 | g_srv_modelindex = datamap_offset("m_nModelIndex", 7); // FIELD_SHORT |
| 110 | g_srv_absvelocity = datamap_offset("m_vecAbsVelocity", 3); // FIELD_VECTOR | ||
| 111 | g_srv_velocity = datamap_offset("m_vecVelocity", 3); // FIELD_VECTOR | ||
| 108 | g_srv_ready = g_srv_health > 0; | 112 | g_srv_ready = g_srv_health > 0; |
| 109 | if (g_srv_ready) { | 113 | if (g_srv_ready) { |
| 110 | logf("server datamap (%s): health=%d maxhealth=%d lifeste=%d " | 114 | logf("server datamap (%s): health=%d maxhealth=%d lifeste=%d " |
| 111 | "modelindex=%d", | 115 | "modelindex=%d absvel=%d vel=%d", |
| 112 | g_server.path.c_str(), g_srv_health, g_srv_maxhealth, g_srv_lifestate, | 116 | g_server.path.c_str(), g_srv_health, g_srv_maxhealth, g_srv_lifestate, |
| 113 | g_srv_modelindex); | 117 | g_srv_modelindex, g_srv_absvelocity, g_srv_velocity); |
| 114 | fail_logged = false; | 118 | fail_logged = false; |
| 115 | } else if (!fail_logged) { | 119 | } else if (!fail_logged) { |
| 116 | fail_logged = true; | 120 | fail_logged = true; |
| @@ -175,3 +179,18 @@ int server_model_index(void *base) { | |||
| 175 | return -1; | 179 | return -1; |
| 176 | return (int)*(short *)((uintptr_t)base + g_srv_modelindex); | 180 | return (int)*(short *)((uintptr_t)base + g_srv_modelindex); |
| 177 | } | 181 | } |
| 182 | |||
| 183 | // Set the authoritative vertical velocity of a server entity. m_vecAbsVelocity | ||
| 184 | // is what CGameMovement::SetupMove reads through GetAbsVelocity(); writing the | ||
| 185 | // networked m_vecVelocity directly is deliberately avoided because the server | ||
| 186 | // datamap scan can find a same-named field on another class first. The movement | ||
| 187 | // code syncs m_vecVelocity through SetAbsVelocity after the command. | ||
| 188 | bool server_set_velocity_z(int idx, float vz) { | ||
| 189 | if (!g_srv_ready || !g_iface.engineserver || g_srv_absvelocity <= 0) | ||
| 190 | return false; | ||
| 191 | void *base = nullptr; | ||
| 192 | if (server_entity_health(idx, nullptr, nullptr, &base) < 0 || !base) | ||
| 193 | return false; | ||
| 194 | *(float *)((uintptr_t)base + g_srv_absvelocity + 8) = vz; | ||
| 195 | return true; | ||
| 196 | } | ||
