dwgx@blog:~$dwgx
> cd ../posts

cs2-walkbot-ext: An external CS2 walkbot whose aim moves like a human hand

// created

An external CS2 patrol bot that never injects a DLL and never hooks game functions. It just uses ReadProcessMemory to read the state out of cs2.exe, then synthesizes keyboard and mouse input from outside the process with SendInput. Memory is read-only, input is driven in from the outside.

There’s really only one problem I wanted to solve: how to make a machine-driven mouse not look like a machine. The crosshair on off-the-shelf cheats snaps onto the target in one shot, a linear teleport that’s fake at a glance. What I wanted was that human-hand curve, the wind-up, the glide, the convergence at the point, with a little jitter on top. Most of the fun was in that.

Aiming is written three ways, switchable at runtime. Legacy Smooth came first, a scalar EMA, good enough but very numeric. Adaptive Servo is built as servo control, PID plus jerk limiting, and finally has a bit of a physical feel. Humanized is the one I’m happiest with: WindMouse wind noise paired with Fitts’s law to compute target speed, so start-glide-settle grows naturally, then tremor, drift, and a random kick are layered on, and running it really does look like a human hand. I also added motion prediction and an emergency brake, counter-driving on stop to cancel the glide, otherwise it overshoots the point. Enemy support tracks the nearest one, and it can also treat teammates as enemies.

Movement runs along a recorded waypoint map. Ordinary points connect directly, branch points run in three modes, Sequential / Random / Cycle, all landing in paths.json. There are also a few Python scripts to generate paths, extracting them from demo trajectories or pulling the Mirage centerline, fully decoupled from the C++ main program and not part of the build.

The core is C++20, a Windows console, with the UI in Dear ImGui + DX11. The overlay runs on its own thread, WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST for click-through, and uses WorldToScreen to project the waypoints onto the screen and draw them. Running it needs administrator rights, since ReadProcessMemory requires elevation; in game, F6 starts and F7 stops.

The data flow looks roughly like this:

cs2.exe memory ─┐
                ├─> interfaces ─> walkbot_core (state) ─> path_runner (decision) ─> SendInput
process/memory ─┘                        │
                                         └─> Overlay Thread ─> WorldToScreen

The biggest pitfall comes down to one word: offsets. When CS2 updates, the offsets and signatures dumped from the SDK can break. Maintenance is narrowed to two spots, the pattern-scan signatures in interfaces.h::Initialize() and the MEM_PAD(offset) in sdk/*.hpp; after an update you basically just patch those two. It’s a purely experimental thing, no tests, no CI, no linter, and when offsets break you patch them by hand. As a project to sharpen the skills, it’s well worth it.