From aa096fe6886681d3338573cd3af779b57a5b56ab Mon Sep 17 00:00:00 2001 From: salmonik Date: Sun, 30 Aug 2026 03:32:59 +0200 Subject: init --- src/netvars.cpp | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 src/netvars.cpp (limited to 'src/netvars.cpp') diff --git a/src/netvars.cpp b/src/netvars.cpp new file mode 100644 index 0000000..41070c7 --- /dev/null +++ b/src/netvars.cpp @@ -0,0 +1,185 @@ +// netvars.cpp — ClientClass walk, RecvTable resolution, movetype from pred-TD +#include "hl2h.h" + +// RecvTable layout: pProps@0, nProps@4, pDecoder@8, pNetTableName@0xC +// RecvProp layout: pVarName@0, pArrayProp@0x18, pDataTable@0x28, offset@0x2C +// ClientClass (2004-era): CreateFn@0, CreateEventFn@4, Name@8, RecvTable@0xC, Next@0x10 + +static bool module_string(uintptr_t p, char *out, size_t outn) { + if (!g_client.contains(p)) + return false; + const char *s = (const char *)p; + size_t i = 0; + while (i < outn - 1) { + unsigned char ch = (unsigned char)s[i]; + if (ch == 0) + break; + if (ch < 0x20 || ch > 0x7E) + return false; + out[i] = (char)ch; + i++; + } + if (i == 0 || i >= outn - 1) + return false; + out[i] = 0; + return true; +} + +static int g_dumped_tables = 0; + +static void collect_netvars(uintptr_t table, uintptr_t base_off, int depth) { + if (depth > 8) + return; + uintptr_t props = *(uintptr_t *)(table + 0x00); + int32_t nprops = *(int32_t *)(table + 0x04); + if (!props || !g_client.contains(props) || nprops <= 0 || nprops > 512) + return; + + char tname[64]; + if (!module_string(*(uintptr_t *)(table + 0x0C), tname, sizeof(tname))) + return; + if (strncmp(tname, "DT_", 3) != 0) + return; + + bool dump = g_dumped_tables < 2 && + (strcmp(tname, "DT_BaseEntity") == 0 || + strcmp(tname, "DT_BasePlayer") == 0); + if (dump) { + g_dumped_tables++; + logf("dump %s: %d props @ table %#lx (base_off %#lx)", tname, nprops, + (unsigned long)table, (unsigned long)base_off); + } + + for (int32_t i = 0; i < nprops; i++) { + uintptr_t prop = props + (size_t)i * 0x3C; + if (!g_client.contains(prop)) + return; + uintptr_t pname = *(uintptr_t *)(prop + 0x00); + int32_t ptype = *(int32_t *)(prop + 0x04); + int32_t poff = *(int32_t *)(prop + 0x2C); + uintptr_t parray = *(uintptr_t *)(prop + 0x18); + uintptr_t ptable = *(uintptr_t *)(prop + 0x28); + + char pname2[64]; + if (!module_string(pname, pname2, sizeof(pname2))) + continue; + + if (dump) + logf(" [%s] %s type=%d off=%d(0x%x) table=%s array=%s", tname, pname2, + ptype, poff, poff, ptable ? "yes" : "no", parray ? "yes" : "no"); + + if (ptable) { + collect_netvars(ptable, base_off + (uint32_t)poff, depth + 1); + continue; + } + + if (strcmp(pname2, "m_fFlags") == 0 && g_net.flags < 0) { + g_net.flags = (int)(base_off + (uint32_t)poff); + } else if (strcmp(pname2, "m_vecVelocity") == 0 && g_net.velocity < 0) { + if (parray && g_client.contains(parray)) { + int32_t eoff = *(int32_t *)(parray + 0x2C); + g_net.velocity = (int)(base_off + (uint32_t)(eoff ? eoff : poff)); + } else { + g_net.velocity = (int)(base_off + (uint32_t)poff); + } + } + if (g_net.ok()) + return; + } +} + +// prediction datamap scan for m_MoveType (FIELD_CHARACTER, fieldOffset in range) +static bool resolve_movetype() { + static const char needle[] = "m_MoveType"; + const size_t nlen = sizeof(needle); + + uintptr_t str_va = 0; + for (auto &r : g_client.ranges) { + const uint8_t *base = (const uint8_t *)r.lo; + size_t span = r.hi - r.lo; + for (size_t i = 0; i + nlen <= span; i++) { + if (memcmp(base + i, needle, nlen) == 0) { + str_va = r.lo + i; + break; + } + } + if (str_va) + break; + } + if (!str_va) { + logf("m_MoveType string not found"); + return false; + } + + uint32_t offs[16]; + int counts[16] = {}; + int ncand = 0; + const uint32_t target = (uint32_t)str_va; + for (auto &r : g_client.ranges) { + const uint8_t *base = (const uint8_t *)r.lo; + size_t span = r.hi - r.lo; + for (size_t off = 4; off + 8 <= span; off += 4) { + if (*(const uint32_t *)(base + off) != target) + continue; + uint32_t ftype = *(const uint32_t *)(base + off - 4); + int32_t foff = *(const int32_t *)(base + off + 4); + if (ftype != 8 || foff < 0x40 || foff > 0x400) + continue; + int k; + for (k = 0; k < ncand; k++) + if (offs[k] == (uint32_t)foff) + break; + if (k == ncand && ncand < 16) { + offs[ncand] = (uint32_t)foff; + ncand++; + } + if (k < ncand) + counts[k]++; + } + } + if (ncand == 0) { + logf("m_MoveType typedescription not found"); + return false; + } + int best = 0; + for (int k = 1; k < ncand; k++) + if (counts[k] > counts[best]) + best = k; + g_net.movetype = (int)offs[best]; + return true; +} + +void resolve_netvars() { + using GetAllClassesFn = void *(*)(void *); + uintptr_t head = + (uintptr_t)vfunc(g_iface.client, 8)(g_iface.client); + if (!g_client.contains(head)) { + logf("GetAllClasses returned %p, outside client module", (void *)head); + return; + } + int classes = 0; + for (uintptr_t cc = head; cc && g_client.contains(cc) && classes < 1024; + cc = *(uintptr_t *)(cc + 0x10)) { + classes++; + char name[64]; + if (!module_string(*(uintptr_t *)(cc + 0x08), name, sizeof(name))) + break; + collect_netvars(*(uintptr_t *)(cc + 0x0C), 0, 0); + if (g_net.flags >= 0 && g_net.velocity >= 0) + break; + } + logf("client classes walked: %d", classes); + + if (g_net.movetype < 0 && !resolve_movetype()) { + // movetype unresolved — bhop still works, just no noclip/ladder guard + logf("movetype guard disabled"); + } + + if (g_net.ok()) { + logf("netvars: m_fFlags=+0x%X m_vecVelocity[0]=+0x%X m_MoveType=+0x%X", + g_net.flags, g_net.velocity, g_net.movetype); + } else { + logf("netvar resolution incomplete: flags=%d velocity=%d movetype=%d", + g_net.flags, g_net.velocity, g_net.movetype); + } +} -- cgit v1.2.3