- Status
- Offline
- Joined
- Mar 3, 2026
- Messages
- 690
- Reaction score
- 457
Tired of seeing the scene stagnate with the same old movement logic. If you're working on rage features for GoldSource, you've likely run into the simulation issues when pushing the engine limits. I'm dropping some logic I've been sitting on that addresses how the client handles ignored commands.
The Logic Behind the Stuck Flag
When SV_CheckCmdTimes is triggered on the server side and your commands start getting ignored, the game engine stops simulating your player. On the client, this manifests as delta packets that only contain the header size. We can detect this state easily in CL_Move (pre-original) to determine if the server has effectively "frozen" our simulation.
Fixing Rubberbanding via CL_PredictMove
To stop the annoying rubberbanding effect when using speedhacks or hitting command limits, we need to intercept the player simulation. In GoldSource, CL_PredictMove is what actually drives the local simulation. The fix is to prevent this function from running when we are tagged as stuck. If our client packet bytes are just the header size (3 bytes), we should skip the client-side simulation entirely.
This is basic engine behavior, but most people just paste a standard speedhack and wonder why the screen jitters like crazy. Clean up your movement code and the results speak for themselves.
Anyone else found a cleaner way to handle the delta descriptors without hardcoding the 3-byte check?
The Logic Behind the Stuck Flag
When SV_CheckCmdTimes is triggered on the server side and your commands start getting ignored, the game engine stops simulating your player. On the client, this manifests as delta packets that only contain the header size. We can detect this state easily in CL_Move (pre-original) to determine if the server has effectively "frozen" our simulation.
Code:
// Execute this in CL_Move pre-original
game_i.is_stuck = local && c_game::is_alive(local) && game_i.client_state->frames[game_i.client_state->parsecountmod].clientbytes == 3;
Fixing Rubberbanding via CL_PredictMove
To stop the annoying rubberbanding effect when using speedhacks or hitting command limits, we need to intercept the player simulation. In GoldSource, CL_PredictMove is what actually drives the local simulation. The fix is to prevent this function from running when we are tagged as stuck. If our client packet bytes are just the header size (3 bytes), we should skip the client-side simulation entirely.
Code:
// Offsets for the latest Steam build of CS 1.6
make_hook(void, __cdecl, get_module_base("hw.dll") + 0x1AB760, CL_PredictMove, bool predicting)
{
// Kill simulation if the server isn't processing our cmds
if (game_i.is_stuck)
{
return;
}
original.call(predicting);
}
- By checking the stuck flag before the CL_Move original call, we align with the pipeline: CL_Move -> CL_PredictMove -> CL_ReadPackets. This ensures we are essentially one frame behind the server, preventing desync.
- We hook CL_PredictMove instead of CL_RunUsercmd because the latter handles frame copying. If you kill CL_RunUsercmd, you'll face weird interpolation issues and broken state transitions.
- The 3-byte check for clientbytes refers to the DELTA descriptors. When the player isn't being simulated, there are zero changes transmitted—no sequence updates, nothing. It's just the raw header.
This is basic engine behavior, but most people just paste a standard speedhack and wonder why the screen jitters like crazy. Clean up your movement code and the results speak for themselves.
Anyone else found a cleaner way to handle the delta descriptors without hardcoding the 3-byte check?