- Status
- Offline
- Joined
- Mar 3, 2026
- Messages
- 805
- Reaction score
- 457
Reversing the T6 Engine — Internal Logic for BO2
If you are still messing around with the Black Ops 2 codebase, you know the T6 engine is a classic playground for internal research. Whether you are building a simple ESP or a rage-bot, getting the entity list right and handling bones without desync is the first hurdle. Below is a breakdown of the entity structures, bone resolution via internal strings, and a NoSpread hook using hardware breakpoints.
Entity List Iteration
The entity array is handled through the CGS structure. You want to pull the base and iterate through the 56 slots. Note the stride—it is a fixed 0x380 (896) bytes.
Bone Retrieval & Tag Resolution
Fetching positions isn't just about reading a vector. You need to resolve bone strings to internal tag handles. The engine uses SL_FindString at 0x529840. For a clean ESP, you’ll focus on tags like "j_head", "j_helmet", and the ankle joints.
Once the tag is resolved, call CG_DObjGetWorldTagPos to get the actual world coordinates. Use the entity pose structure, which is generally just the entity pointer casted.
Implementing NoSpread via HWBP
The most effective way to kill spread is to hook 0x42E950 (Shoot function) and zero out the X and Y output floats. Instead of a messy VMT swap or raw patch that might get flagged, using a Hardware Breakpoint (HWBP) with a Vectored Exception Handler (VEH) is a much smoother approach.
Architecture Notes
Keep in mind that while the game is older, the T6 engine still has some quirks with how bone transforms are updated in memory—sometimes you'll need to wait for a frame update to get the latest tag positions.
Drop a comment if you've found a more stable offset for the client state in the latest Steam build.
If you are still messing around with the Black Ops 2 codebase, you know the T6 engine is a classic playground for internal research. Whether you are building a simple ESP or a rage-bot, getting the entity list right and handling bones without desync is the first hurdle. Below is a breakdown of the entity structures, bone resolution via internal strings, and a NoSpread hook using hardware breakpoints.
Entity List Iteration
The entity array is handled through the CGS structure. You want to pull the base and iterate through the 56 slots. Note the stride—it is a fixed 0x380 (896) bytes.
Code:
uintptr_t cgs = RPM::Read<uintptr_t>(0x1115688 + (client << 2));
uintptr_t entPtr = cgs;
for (int i = 0; i < 56; i++, entPtr += 0x380)
{
uint8_t valid = RPM::Read<uint8_t>(entPtr + 0x378);
if (!(valid & 2)) continue;
uint16_t type = RPM::Read<uint16_t>(entPtr + 0x2B4);
// ET_PLAYER = 1, ET_ACTOR (Zombies) = 16
if (type != 1 && type != 16) continue;
float* origin = RPM::Ptr<float>(entPtr + 0x2C);
int dobjHandle = RPM::Read<int>(entPtr + 0x1DC);
}
Bone Retrieval & Tag Resolution
Fetching positions isn't just about reading a vector. You need to resolve bone strings to internal tag handles. The engine uses SL_FindString at 0x529840. For a clean ESP, you’ll focus on tags like "j_head", "j_helmet", and the ankle joints.
"j_head","j_helmet","j_ankle_le","j_ankle_ri",
"j_ball_le","j_ball_ri","j_clavicle_le","j_clavicle_ri",
"j_elbow_le","j_elbow_ri","j_hip_le","j_hip_ri",
"j_knee_le","j_knee_ri","j_mainroot","j_neck",
"j_palm_ri","j_shoulder_le","j_shoulder_ri","j_spine4",
"j_spinelower","j_spineupper","j_wrist_le","j_wrist_ri",
"j_wristtwist_le","j_wristtwist_ri"
"j_ball_le","j_ball_ri","j_clavicle_le","j_clavicle_ri",
"j_elbow_le","j_elbow_ri","j_hip_le","j_hip_ri",
"j_knee_le","j_knee_ri","j_mainroot","j_neck",
"j_palm_ri","j_shoulder_le","j_shoulder_ri","j_spine4",
"j_spinelower","j_spineupper","j_wrist_le","j_wrist_ri",
"j_wristtwist_le","j_wristtwist_ri"
Once the tag is resolved, call CG_DObjGetWorldTagPos to get the actual world coordinates. Use the entity pose structure, which is generally just the entity pointer casted.
Implementing NoSpread via HWBP
The most effective way to kill spread is to hook 0x42E950 (Shoot function) and zero out the X and Y output floats. Instead of a messy VMT swap or raw patch that might get flagged, using a Hardware Breakpoint (HWBP) with a Vectored Exception Handler (VEH) is a much smoother approach.
Code:
int __cdecl NoSpread(int a1, int a2, float* x, float* y)
{
if (x) *x = 0.0f;
if (y) *y = 0.0f;
return 0;
}
// Setup your hardware breakpoint on 0x42E950 and redirect XIP/EIP to this handler.
Architecture Notes
- Viewmatrix: Found at 0x0103AD40. It’s column-major. If you’re getting desync on the internal W2S, just calculate it yourself using this matrix.
- Zombies: Actors (type 16) are basically entities too. This list handles them perfectly if you're writing a trainer for the survival maps.
- Scaling: For Box ESP, use the distance between helmet and ball tags to scale your box dynamically based on the model's skeleton rather than a static value.
Keep in mind that while the game is older, the T6 engine still has some quirks with how bone transforms are updated in memory—sometimes you'll need to wait for a frame update to get the latest tag positions.
Drop a comment if you've found a more stable offset for the client state in the latest Steam build.