From 471f022f449a18766fedb1ff0a4204a933b95b9c Mon Sep 17 00:00:00 2001 From: salmonik Date: Tue, 27 Oct 2026 13:48:27 +0100 Subject: Jumpbug: optional velocity-timed assist The unduck window is only 2 units, while a 625-ups fall moves ~9.3 units per 66.7 Hz movement command, so pure jumpbug is ~20% reliable at that speed. - Add Jumpbug -> Assist timing (default off). When the natural next command would skip [36,38], pin the post-command ducked distance to 37 by writing the vertical velocity for that command. Pure jumpbug remains available with the option off. - Resolve the client local m_vecAbsVelocity (0x11C) via the client datamap and the server m_vecAbsVelocity (0x228) via the server datamap. - Write both the authoritative server velocity (what SetupMove reads through GetAbsVelocity) and the local prediction fields, so the next CreateMove sees the assisted origin on both sides. - Do not write the server m_vecVelocity directly: the datamap scan finds two same-named entries (0x1C and 0x29C); SetAbsVelocity syncs it after movement. --- src/server.cpp | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'src/server.cpp') 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; int g_srv_maxhealth = -1; int g_srv_lifestate = -1; int g_srv_modelindex = -1; +int g_srv_absvelocity = -1; +int g_srv_velocity = -1; static bool g_srv_ready = false; static bool g_srv_checked = false; @@ -105,12 +107,14 @@ bool resolve_server_netvars() { g_srv_maxhealth = datamap_offset("m_iMaxHealth", FIELD_INTEGER); g_srv_lifestate = datamap_offset("m_lifeState", FIELD_CHARACTER); g_srv_modelindex = datamap_offset("m_nModelIndex", 7); // FIELD_SHORT + g_srv_absvelocity = datamap_offset("m_vecAbsVelocity", 3); // FIELD_VECTOR + g_srv_velocity = datamap_offset("m_vecVelocity", 3); // FIELD_VECTOR g_srv_ready = g_srv_health > 0; if (g_srv_ready) { logf("server datamap (%s): health=%d maxhealth=%d lifeste=%d " - "modelindex=%d", + "modelindex=%d absvel=%d vel=%d", g_server.path.c_str(), g_srv_health, g_srv_maxhealth, g_srv_lifestate, - g_srv_modelindex); + g_srv_modelindex, g_srv_absvelocity, g_srv_velocity); fail_logged = false; } else if (!fail_logged) { fail_logged = true; @@ -175,3 +179,18 @@ int server_model_index(void *base) { return -1; return (int)*(short *)((uintptr_t)base + g_srv_modelindex); } + +// Set the authoritative vertical velocity of a server entity. m_vecAbsVelocity +// is what CGameMovement::SetupMove reads through GetAbsVelocity(); writing the +// networked m_vecVelocity directly is deliberately avoided because the server +// datamap scan can find a same-named field on another class first. The movement +// code syncs m_vecVelocity through SetAbsVelocity after the command. +bool server_set_velocity_z(int idx, float vz) { + if (!g_srv_ready || !g_iface.engineserver || g_srv_absvelocity <= 0) + return false; + void *base = nullptr; + if (server_entity_health(idx, nullptr, nullptr, &base) < 0 || !base) + return false; + *(float *)((uintptr_t)base + g_srv_absvelocity + 8) = vz; + return true; +} -- cgit v1.2.3