Compare commits

..

10 Commits

Author SHA1 Message Date
rtldg
31eb8e3b5b Update CMoveData offsets (& add some x64 offsets)
Co-Authored-By: zamounet <23285283+zamounet@users.noreply.github.com>
2025-02-25 20:10:51 -05:00
rtldg
1df7099bbc Update gamedata for cstrike 2025-02-18 update (only 32-bit for now) 2025-02-25 20:10:51 -05:00
rio
c233d0bcc3
Merge pull request #10 from GAMMACASE/master
Update gamedata after latest CSGO update (22/09/21)
2021-09-22 18:14:44 -04:00
GAMMACASE
ad6a3d5379 Update gamedata after latest CSGO update (22/09/21) 2021-09-22 18:34:23 +03:00
rio
bdd2f7fda5 I cant count 2018-12-07 00:26:41 -06:00
rio
ba2e9d22a5 Update gamedata for CSGO's Danger Zone update 2018-12-06 20:50:49 -06:00
rio
1c85683a7e Avoid a side-effect of TeleportEntity (fixes #2) 2018-10-09 15:30:13 -05:00
rio
0e06cb0c81 Small fix for old Slopefix logic setting 2018-10-08 19:52:17 -05:00
rio
46cabbe2d6 Update technical overview 2018-10-07 21:34:05 -05:00
rio
3aea927d8a Small typo 2018-10-07 21:17:07 -05:00
3 changed files with 196 additions and 64 deletions

View File

@ -5,10 +5,10 @@
"Keys"
{
"IGameMovement" "GameMovement001"
"IServerGameEnts" "ServerGameEnts001"
}
"Signatures"
{
"CreateInterface"
@ -18,7 +18,7 @@
"linux" "@CreateInterface"
}
}
"Offsets"
{
"ProcessMovement"
@ -28,7 +28,7 @@
}
}
}
"csgo"
{
"Offsets"
@ -36,48 +36,124 @@
// applies to trigger_vphysics_motion and trigger_wind
"CBaseVPhysicsTrigger::PassesTriggerFilters"
{
"windows" "196"
"linux" "197"
"windows" "200"
"linux" "201"
}
// applies to all other triggers
"CBaseTrigger::PassesTriggerFilters"
{
"windows" "206"
"linux" "207"
"windows" "210"
"linux" "211"
}
"IServerGameEnts::MarkEntitiesAsTouching"
{
"windows" "1"
"linux" "2"
}
"CMoveData::m_flForwardMove"
{
"windows" "44"
"linux" "44"
}
"CMoveData::m_flSideMove"
{
"windows" "48"
"linux" "48"
}
"CMoveData::m_flMaxSpeed"
{
"windows" "56"
"linux" "56"
}
"CMoveData::m_vecVelocity"
{
"windows" "64"
"linux" "64"
}
"CMoveData::m_vecAbsOrigin"
{
"windows" "172"
"linux" "172"
}
}
}
"cstrike"
{
"Offsets"
{
{
"CMoveData::m_flForwardMove"
{
"windows" "44"
"windows64" "44"
"linux" "44"
"linux64" "44"
}
"CMoveData::m_flSideMove"
{
"windows" "52"
"windows64" "52"
"linux" "52"
"linux64" "52"
}
"CMoveData::m_flMaxSpeed"
{
"windows" "60"
"windows64" "60"
"linux" "60"
"linux64" "60"
}
"CMoveData::m_vecVelocity"
{
"windows" "68"
"windows64" "68"
"linux" "68"
"linux64" "68"
}
"CMoveData::m_vecAbsOrigin"
{
"windows" "156"
"windows64" "156"
"linux" "156"
"linux64" "156"
}
// applies to trigger_vphysics_motion and trigger_wind
"CBaseVPhysicsTrigger::PassesTriggerFilters"
{
"windows" "188"
"linux" "189"
"windows" "194"
"windows64" "194"
"linux" "195"
"linux64" "195"
}
// applies to all other triggers
"CBaseTrigger::PassesTriggerFilters"
{
"windows" "197"
"linux" "198"
"windows" "203"
"windows64" "203"
"linux" "204"
"linux64" "204"
}
"IServerGameEnts::MarkEntitiesAsTouching"
{
"windows" "2"
"windows64" "2"
"linux" "3"
"linux64" "3"
}
}
}
}
}

View File

@ -6,7 +6,7 @@
#pragma semicolon 1
#pragma newdecls required
#define PLUGIN_VERSION "1.1.1"
#define PLUGIN_VERSION "1.1.3"
public Plugin myinfo =
{
@ -48,7 +48,7 @@ int g_iLastGroundEnt[MAXPLAYERS+1];
int g_iLastLandTick[MAXPLAYERS+1];
int g_iLastCollisionTick[MAXPLAYERS+1];
int g_iLastMapTeleportTick[MAXPLAYERS+1];
int g_bMapTeleportedSequentialTicks[MAXPLAYERS+1];
bool g_bMapTeleportedSequentialTicks[MAXPLAYERS+1];
float g_vCollisionPoint[MAXPLAYERS+1][3];
float g_vCollisionNormal[MAXPLAYERS+1][3];
@ -86,6 +86,12 @@ Handle g_hProcessMovementHookPre;
Address g_IServerGameEnts;
Handle g_hMarkEntitiesAsTouching;
int g_iCMoveData_ForwardMove;
int g_iCMoveData_SideMove;
int g_iCMoveData_MaxSpeed;
int g_iCMoveData_Velocity;
int g_iCMoveData_Origin;
bool g_bIsSurfMap;
bool g_bLateLoad;
@ -223,6 +229,27 @@ public void OnPluginStart()
DHookAddParam(g_hProcessMovementHookPre, HookParamType_ObjectPtr);
DHookRaw(g_hProcessMovementHookPre, false, IGameMovement);
if ((g_iCMoveData_ForwardMove = GameConfGetOffset(gamedataConf, "CMoveData::m_flForwardMove")) == -1)
{
SetFailState("Failed to get CMoveData::m_flForwardMove");
}
if ((g_iCMoveData_SideMove = GameConfGetOffset(gamedataConf, "CMoveData::m_flSideMove")) == -1)
{
SetFailState("Failed to get CMoveData::m_flSideMove");
}
if ((g_iCMoveData_MaxSpeed = GameConfGetOffset(gamedataConf, "CMoveData::m_flMaxSpeed")) == -1)
{
SetFailState("Failed to get CMoveData::m_flMaxSpeed");
}
if ((g_iCMoveData_Velocity = GameConfGetOffset(gamedataConf, "CMoveData::m_vecVelocity")) == -1)
{
SetFailState("Failed to get CMoveData::m_vecVelocity");
}
if ((g_iCMoveData_Origin = GameConfGetOffset(gamedataConf, "CMoveData::m_vecAbsOrigin")) == -1)
{
SetFailState("Failed to get CMoveData::m_vecAbsOrigin");
}
// MarkEntitiesAsTouching
if (!GameConfGetKeyValue(gamedataConf, "IServerGameEnts", interfaceName, sizeof(interfaceName)))
{
@ -486,7 +513,7 @@ void AirAccelerate(int client, float velocity[3], Handle hParams)
for (int i = 0; i < 2; i++) wishvel[i] = fore[i] * g_vVel[client][0] + side[i] * g_vVel[client][1];
float wishspeed = NormalizeVector(wishvel, wishdir);
float m_flMaxSpeed = DHookGetParamObjectPtrVar(hParams, 2, 56, ObjectValueType_Float);
float m_flMaxSpeed = DHookGetParamObjectPtrVar(hParams, 2, g_iCMoveData_MaxSpeed, ObjectValueType_Float);
if (wishspeed > m_flMaxSpeed && m_flMaxSpeed != 0.0) wishspeed = m_flMaxSpeed;
if (wishspeed)
@ -565,7 +592,7 @@ void PreventCollision(int client, Handle hParams, const float origin[3], const f
// Since the MoveData for this tick has already been filled and is about to be used, we need
// to modify it directly instead of changing the player entity's actual position (such as with TeleportEntity).
DHookSetParamObjectPtrVarVector(hParams, 2, GetEngineVersion() == Engine_CSGO ? 172 : 152, ObjectValueType_Vector, newOrigin);
DHookSetParamObjectPtrVarVector(hParams, 2, g_iCMoveData_Origin, ObjectValueType_Vector, newOrigin);
DebugLaser(client, origin, newOrigin, 15.0, 0.5, g_color2);
@ -589,20 +616,28 @@ void ClipVelocity(const float velocity[3], const float nrm[3], float out[3])
// The adjust step only matters with overbounce which doesnt apply to walkable surfaces.
}
void SetVelocity(int client, float velocity[3])
void SetVelocity(int client, float velocity[3], bool dontUseTeleportEntity = false)
{
// Pull out basevelocity from desired true velocity
// Use the pre-tick basevelocity because that is what influenced this tick's movement and the desired new velocity.
SubtractVectors(velocity, g_vLastBaseVelocity[client], velocity);
float baseVelocity[3];
GetEntPropVector(client, Prop_Data, "m_vecBaseVelocity", baseVelocity);
if (dontUseTeleportEntity && GetEntPropEnt(client, Prop_Data, "m_hMoveParent") == -1)
{
SetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", velocity);
SetEntPropVector(client, Prop_Data, "m_vecVelocity", velocity);
}
else
{
float baseVelocity[3];
GetEntPropVector(client, Prop_Data, "m_vecBaseVelocity", baseVelocity);
TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, velocity);
TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, velocity);
// TeleportEntity with non-null velocity wipes out basevelocity, so restore it after.
// Since we didn't change position, nothing should change regarding influences on basevelocity.
SetEntPropVector(client, Prop_Data, "m_vecBaseVelocity", baseVelocity);
// TeleportEntity with non-null velocity wipes out basevelocity, so restore it after.
// Since we didn't change position, nothing should change regarding influences on basevelocity.
SetEntPropVector(client, Prop_Data, "m_vecBaseVelocity", baseVelocity);
}
}
public MRESReturn DHook_ProcessMovementPre(Handle hParams)
@ -642,18 +677,20 @@ void RunPreTickChecks(int client, Handle hParams)
g_iButtons[client] = DHookGetParamObjectPtrVar(hParams, 2, 36, ObjectValueType_Int);
g_iOldButtons[client] = DHookGetParamObjectPtrVar(hParams, 2, 40, ObjectValueType_Int);
DHookGetParamObjectPtrVarVector(hParams, 2, 44, ObjectValueType_Vector, g_vVel[client]);
g_vVel[client][0] = DHookGetParamObjectPtrVar(hParams, 2, g_iCMoveData_ForwardMove, ObjectValueType_Float);
g_vVel[client][1] = DHookGetParamObjectPtrVar(hParams, 2, g_iCMoveData_SideMove, ObjectValueType_Float);
g_vVel[client][2] = 0.0;
DHookGetParamObjectPtrVarVector(hParams, 2, 12, ObjectValueType_Vector, g_vAngles[client]);
float velocity[3];
DHookGetParamObjectPtrVarVector(hParams, 2, 64, ObjectValueType_Vector, velocity);
DHookGetParamObjectPtrVarVector(hParams, 2, g_iCMoveData_Velocity, ObjectValueType_Vector, velocity);
float baseVelocity[3];
// basevelocity is not stored in MoveData
GetEntPropVector(client, Prop_Data, "m_vecBaseVelocity", baseVelocity);
float origin[3];
DHookGetParamObjectPtrVarVector(hParams, 2, GetEngineVersion() == Engine_CSGO ? 172 : 152, ObjectValueType_Vector, origin);
DHookGetParamObjectPtrVarVector(hParams, 2, g_iCMoveData_Origin, ObjectValueType_Vector, origin);
float nextOrigin[3], mins[3], maxs[3];
@ -998,7 +1035,9 @@ bool DoInclineCollisionFixes(int client, const float nrm[3])
// If a collision was predicted this tick (and wasn't prevented by another fix alrady), no fix is needed.
// It's possible we actually have to run the edge bug fix and an incline fix in the same tick.
if (g_iLastCollisionTick[client] == g_iTick[client]) return false;
// If using the old Slopefix logic, do the fix regardless of necessity just like Slopefix
// so we can be sure to trigger a double boost if applicable.
if (g_iLastCollisionTick[client] == g_iTick[client] && !g_cvUseOldSlopefixLogic.BoolValue) return false;
// Make sure the ground is not level, otherwise a collision would do nothing important anyway.
if (nrm[2] == 1.0) return false;
@ -1094,9 +1133,24 @@ bool DoTelehopFix(int client)
// Don't forget to add the second half-tick of gravity ourselves.
FinishGravity(client, newVelocity);
DebugMsg(client, "DO FIX: Telehop");
float origin[3];
GetEntPropVector(client, Prop_Data, "m_vecAbsOrigin", origin);
SetVelocity(client, newVelocity);
float mins[3], maxs[3];
GetEntPropVector(client, Prop_Data, "m_vecMins", mins);
GetEntPropVector(client, Prop_Data, "m_vecMaxs", maxs);
TR_TraceHullFilter(origin, origin, mins, maxs, MASK_PLAYERSOLID, PlayerFilter);
// If we appear to be "stuck" after teleporting (likely because the teleport destination
// was exactly on the ground), set velocity directly to avoid side-effects of
// TeleportEntity that can cause the player to really get stuck in the ground.
// This might only be an issue in CSS, but do it on CSGO too just to be safe.
bool dontUseTeleportEntity = TR_DidHit();
DebugMsg(client, "DO FIX: Telehop%s", dontUseTeleportEntity ? " (no TeleportEntity)" : "");
SetVelocity(client, newVelocity, dontUseTeleportEntity);
return true;
}

58
tech.md
View File

@ -5,87 +5,89 @@ This is a simple overview of some key engine steps for reference. Not all of the
1. **`OnPlayerRunCmd`** (SourceMod's public forward) - This function should really only be used for modifying player inputs. If you need to run some code for each client for each command that is actually executed (i.e. where the server is not overloaded), you should probably use a PreThink or PreThinkPost hook. In many cases doing stuff here (or in OnPlayerRunCmdPost) is fine as long as you are aware that this does not have a 1:1 relationship with physically simulated client ticks.
(Note that if the server is overloaded, the command is dropped and the following steps are skipped)
1. **`PreThink/PreThinkPost`** - This is a convenient function to hook that is run right before the command is processed and the player is moved, triggers are touched, etc.
2. **`CGameMovement::ProcessMovement`** - This is where the key and mouse data for this user command are applied and the player is moved one tick
3. **`CGameMovement::ProcessImpacts`** - This is where triggers are actually "touched", note that this is enitrely after movement for this tick has completed
4. **`PostThink/PostThinkPost`** - This is a convenient function to hook that is run right after the command is processed and the player is moved, triggers are touched, etc.
2. **`OnPlayerRunCmdPost`** - This should most correctly be used as a convenient way to check the final inputs for this command after all plugins have potentially modified them. This forward is fired even if the command was actually dropped.
2. **`OnPlayerRunCmdPost`** - This should most correctly be used as a convenient way to check the final inputs for this command after all plugins have potentially modified them. This forward is fired even if the command was actually dropped.
## Technical Overview of Fixes
These fixes are split into two groups: pre-tick fixes -- which are detected and applied immediately *before* a user command is run and a tick is simulated -- and post-tick fixes, which correct the results of a tick immediately *after* it is simulated. This just depends on what I decided was the best way to apply these fixes.
The actions of this plugin are coupled fairly tightly to the engine's movement processing (the most important parts are executed immediately before `CGameMovement::ProcessMovement` is run, the rest happens before each player's `PostThink`) and thus this plugin is unlikely to interfere with other plugins, or be negatively impacted by them. To put it another way, it is safe for other plugins to do whatever they want in `OnPlayerRunCmd/OnPlayerRunCmdPost` and player `PreThink` calls without interfering with these fixes.
In order to understand some of these problems and their fixes, it is relevant to know that the engine will only consider a player "on the ground", and thus able to walk and jump, if their Z velocity is less than positive 140.0. If this is not the case, surfaces that are otherwise shallow enough to walk on will essentially behave like surf ramps because the player is considered in the air rather than standing on them. This is why the player is not able to rapidly bhop up steep inclines when moving faster than a certain speed.
---
**Downhill Inclines** [Post-tick]
If the plugin detects that the player just landed on the ground, but did so without ever colliding with it this tick, the player's velocity is updated to reflect what it would have been had the player actually collided with it. This fix is only applied if a collision would result in the player having a larger absolute amount of horizontal speed than before, which is always the case when falling straight down or moving "downhill", and is *sometimes* the case when moving uphill, especially for steep inclines. This is the same criteria that the original slopefix used to determine when to apply the fix.
The reason it is possible to "land" on the ground without actually touching it is because the engine will consider a player "on the ground" if there is walkable ground *within 2 units* below them at certain times within the simulation of a tick. If it just so happens that you end up within 2 units of a walkable surface at the end of a tick, the engine effectively considers you to have landed on it, which zeroes out your Z velocity immediately with no consideration for the interaction between that Z velocity and the angle of the ground. This issue is more prevalent on **higher** tickrates.
The original slopefix plugin handled this fix slightly differently. Its deflection velocity calculation does not take basevelocity into effect, and more importantly: when the new velocity is applied, any existing basevelocity is baked into the player's velocity immediately, which unfortunately results in a "double boost" if the player jumps on an incline while touching a `trigger_push`. RNGFix handles these things more accurately which eliminates this side-effect, but if you *really* want the old behavior (double boosts) for legacy reasons, set `rngfix_useoldslopefixlogic` to `1` on a case-by-case, per-map basis.
---
**Uphill Inclines** [Pre-tick]
This fix is very much the opposite of the downhill incline fix and aims to guarantee the result that is the opposite of what the downhill incline fix does. Occurrences of this issue are more prevalent on **lower** tickrates.
If the plugin detects that the player *will* collide with an incline (in an "uphill" direction, or into the incline) once this tick is simulated, and it is possible to land on this surface (that is, the surface is not too steep to walk on, and the player's Z velocity at the time of collision is less than positive 140.0), then the player is moved *away* from the incline such that they will barely not collide with the incline by the end of the tick. Note that this adjustment is often only a few units or less and is totally imperceptible to the player in real-time.
This change means that instead of colliding and deflecting along the incline, the player will instead land on the incline without colliding with it. This is desirable because landing immediately zeroes out Z velocity, and the player is able to jump while having the full horizontal velocity they started with. In the event of going up a moderately steep incline, this results in the most favorable possible collision with the surface on the following tick and the greatest possible amount of retained speed when "launching" off the incline.
This change means that instead of colliding and deflecting along the incline, the player will instead land on the incline without colliding with it. This is desirable because landing immediately zeroes out Z velocity, and the player is able to jump while having the full horizontal velocity they started with. In the event of going up a moderately steep incline, this results in the most favorable possible collision with the surface on the following tick and the greatest possible amount of retained speed when "launching" off the incline.
Note that this fix will not be applied if the downhill fix is enabled *and* that fix would result in horizontal speed gain as explained above.
This plugin also gives you the option of normalizing the random behavior of jumping uphill in the opposite way, such that doing so always results in a *collision* with the surface -- and thus a loss of speed. This setting is not recommended, as jumping up even the slightest of inclines can quickly sap player speed, while doing so without the plugin would almost never result in lost speed. To enable this, set `rngfix_uphill` to `-1`. This effectively makes the uphill incline fix function identically to the downhill incline fix, except it is executed even when moving uphill and when doing so results in horizontal speed loss.
---
---
**Edge Bugs** [Pre-tick]
The upcoming tick is simulated to determine if the following are true:
The upcoming tick is simulated to determine if the following are true:
1. The player will collide with a walkable surface -- This is important because the general possibility of being able to land/jump but not actually doing so is what defines this bug
2. After colliding, the player's Z velocity is less than positive 140.0 (a requirement to be able to land, and thus jump rather than slide)
3. Once the *remainder* of the tick is simulated following the collision, the player ends up in a location where there is no ground to land on below them
If all of these are true, the player's position is adjusted such that they will barely avoid colliding with the ground by the end of the tick. This is effectively the same solution as the uphill incline fix, but with different activating conditions. Occurrences of this issue are more prevalent on **lower** tickrates.
---
**Trigger Jumping** [Post-tick]
If the plugin detects that the player just landed on the ground, it determines how far below the player the ground is (which could be as many as 2 units below), finds any triggers that are in this space between the player and the ground, and manually signals to the engine that the player is touching these triggers (if the player was not already touching them).
The rationale behind this is that, if the player is "landed" on the ground, then their hitbox logically must extend all the way to the ground, and thus any triggers in such space should activate. This fix is pretty easy to justify and likely would have been handled better in the engine itself if not for the fact that it *really* only matters in maps made for movement game modes, as thin ground triggers do not come into play in first-party content (and neither does autobhop). Occurrences of this issue are more prevalent on **higher** tickrates.
---
**Telehops** [Post-tick]
If the plugin detects that a `trigger_teleport` was activated during this tick, and either:
If the plugin detects that a `trigger_teleport` was activated during this tick, the player did *not* activate one the previous tick, and either:
* The plugin predicted right before the tick that a collision would occur during this tick (resulting in a change / loss of velocity)
*or*
* The plugin detected that the client landed during the simulation of the tick (resulting in an instant removal of Z velocity)
Then the player's velocity is restored to the velocity they would have had after this tick (including any influence from key and mouse inputs) had the player not collided with -- or landed on -- anything.
The engine simulates each tick in a sequence of discrete steps, which to put it simply starts with a complete simulation of player movement including collisions with any solids, and only *after* this has finished does the engine check to see if the client is touching any triggers and activates them. This means it is not all that unlikely that a player will collide with something inside of or behind a thin `trigger_teleport` before triggering it, despite passing through it to even reach the point of collision. Occurrences of this issue are more prevalent on **lower** tickrates.
This fix is not applied if the player also activated a `trigger_teleport` on the previous tick to account for the speed-stopping teleport hubs some mappers use, especially on surf maps. These hubs typically teleport the player into a tiny box (or even inside a clip brush), and then at that location the player activates another `trigger_teleport` -- or sometimes one of several based on their `targetname` or `classname`. These are explicitly set up to stop the player's speed, and thus the fix should not be applied.
---
**Stair Sliding** [Post-tick]
The plugin checks if the following conditions are true:
1. The plugin predicted that a collision with a vertical surface would occur during this tick.
2. There is walkable ground directly below the point of collision, within the maximum step size (generally, 18.0 units).
3. If the player were to stand on the ground below the point of collision, they would activate no triggers.
4. From the ground below the point of collision, the surface collided with can be stepped up (the step must be as high as the maximum step size at most, there is nothing above the player that prevents the player from traveling up that distance, and the surface on top of the step must be walkable).
If all of these conditions are true, then the player is placed just barely on top of the stair step they just collided with, and the velocity they would have had if they had not collided with the face of the stair step is restored. This issue is mostly unaffected by tickrate.
This fix is only applied on surf maps (maps starting with `surf_`) because it can save a bhopping player from losing all of their speed if they barely hit a small single step even if they had no intention of sliding. Stairs are very uncommonly found on bhop maps, and even then I can't say I've ever seen a staircase that was worth sliding up as part of an optimal route, so the fix is really not needed on bhop anyway.