Skip to content

Instantly share code, notes, and snippets.

Created April 3, 2024 23:27
Show Gist options
  • Save zer0k-z/2eb0c230c8f2c62b5c46d36353cf8d8d to your computer and use it in GitHub Desktop.
Save zer0k-z/2eb0c230c8f2c62b5c46d36353cf8d8d to your computer and use it in GitHub Desktop.
CS2KZ's rampbug fix


In CS2, when a player is sliding/surfing against some geometry, it's possible that they lose all their momentum/velocity. This is usually called wallbug/rampbug. While the name was inherited from Source 1 games (such as CS:GO, CS:S, TF2,...), rampbugs in CS2 behave much differently from its predecessor. In contrast to source 1 games, it's also possible have the velocity redirected to another direction instead of losing all momentum.

This bug doesn't seem to be common in all source 2 games (HL:A for instance, does not have this bug), and was orignially not present in the very early versions of CS2 Limited Test. Rampbugs become more and more frequent over time, with the Call to Arms update effectively doubling the frequency of these bugs, which is a significant problem for custom gamemodes heavily depending on geometry collision (eg. surf).

Keep in mind that while the player collision hitbox is a box, the images shown below will represent the player as a dot instead for simplicity.


From a high level perspective, rampbugs happen when there's a tiny, invisible slope that gets in our way. These slopes are usually no larger than 0.06 units, and sometimes could only be hit by trying to move in certain directions, but they are consistent as long as the requirements are met. These slopes have some properties:

  1. They are all situated at the edges of these mesh triangles, or at the intersection of two triangles.
  2. They are only created once the map is loaded, since the same map will produce different results on different versions of CS2.
  3. Unlike CS:GO rampbugs, the player will not get stuck inside geometry.


There are two ways to approach this problem. From a surf mapper's perspective, they can try to use "convex hull collision" physics then tie an entity to the ramp to minimize rampbugs. This has an advantage of not causing client side prediction errors, but this will not prevent all rampbugs from happening (they are still common at the end of ramps, for instance).

From a plugin maker's perspective, while the low level physics are unknown to the public, the high level movement functions are well understood. In this instance, we can intercept TryPlayerMove and CategorizePosition and move the player in such a way that it can avoid most of the rampbugs. However, since this interception happens at a server level, the player will experience massive prediction errors when a rampbug was prevented on high ping: See this

Plugin solution

The bulk of the code can be found here.


TL;DR: Try to move player slightly away from the ramps before disaster happens.

In this example, we are at point C, trying to move towards the point D. The plane we're supposed to stay on is [AB]. The goal is to be at point G' at the end of the movement.

However, the plane [IJ] is sticking out. We are supposed to end up at the point G at the end, but all our momentum will be annihilated and we'll get stopped at [IJ] instead.

The solution is to keep track of the previous valid plane, then we move the player along the previous plane's normal (or in 26 different directions if this normal fails) to see if we can dodge this mini slope. In this example, this would be our [CE] line.

If we happen to hit a plane, check if it's a a valid plane or one that will potentially knock you away/stop you. A valid plane is the plane with a very similar normal as the previous valid one, or have the normal direction close enough to the previous one that we can consider it "acceptable". If it's not a valid plane, we keep moving away from the original plane. We also prioritize hitting the same original plane, or hitting nothing.

If the parallel trace [EE'] hits something, we make sure the trace is valid, move the player there, and clip the velocity according to the normal that we just got.

If it hits nothing, trace back to the original end point ([E'D] trace). Some scenarios can happen with this new trace:


  1. The trace hits a ramp. We make sure the trace is valid, move the player there, and clip the velocity according to the normal that we just got.

  2. It's possible that the trace still hits nothing. Then we can just move the player straight to the original end spot, no velocity clipping needed.

  3. It's possible that the trace hits the original erroneous plane, as follows. This will make the fix fail...


While non-zero, the chance of this happening is extremely unlikely, and the effect caused by rampbug in this scenario is relatively smaller.

If we moved really far yet we still couldn't dodge this "bugged" ramp, that means the ramp is actually decently big and is most likely a legitimate ramp. In that case, we don't do anything.

This is all done inside TryPlayerMove.

In CategorizePosition, if we are going to land on something and had a valid plane the frame before, we try to move the player a bit in the direction of that plane's normal, to see if what we land on was actually a valid landing spot or not.


While these fixes are effective most of the time, it does not address the root of the problem (invisible ramps sticking out of meshes). These fixes are mostly tested on surf servers, so they might still miss some more edge cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment