From a5e5133b9d0ad35ca491b27ebd1d116493fe7145 Mon Sep 17 00:00:00 2001 From: Dots Date: Mon, 12 Oct 2026 02:22:10 +0300 Subject: [PATCH 1/4] Fix stale collision seed when a TryPlayerMove pre forward changes move data --- addons/sourcemod/scripting/movementapi/hooks.sp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/sourcemod/scripting/movementapi/hooks.sp b/addons/sourcemod/scripting/movementapi/hooks.sp index 9e4778f..53a8a5a 100644 --- a/addons/sourcemod/scripting/movementapi/hooks.sp +++ b/addons/sourcemod/scripting/movementapi/hooks.sp @@ -616,7 +616,8 @@ public MRESReturn DHooks_OnTryPlayerMove_Pre(Address pThis, DHookReturn hReturn, } // Bump 0 can reuse pFirstTrace without tracing. - if (!DHookIsNullParam(hParams, 2)) + // A forward that changed origin or velocity breaks that reuse, which would leave the seed stale. + if (result == Plugin_Continue && !DHookIsNullParam(hParams, 2)) { float fraction = DHookGetParamObjectPtrVar(hParams, 2, TRACE_FRACTION, ObjectValueType_Float); bool allsolid = view_as(DHookGetParamObjectPtrVar(hParams, 2, TRACE_ALLSOLID, ObjectValueType_Bool)); From 534f25bdcc531987413adb43599ae102ef510039 Mon Sep 17 00:00:00 2001 From: Dots Date: Mon, 12 Oct 2026 02:22:11 +0300 Subject: [PATCH 2/4] Fix touch list fallback reporting collisions from an earlier TryPlayerMove --- addons/sourcemod/scripting/movementapi/hooks.sp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/addons/sourcemod/scripting/movementapi/hooks.sp b/addons/sourcemod/scripting/movementapi/hooks.sp index 53a8a5a..c126445 100644 --- a/addons/sourcemod/scripting/movementapi/hooks.sp +++ b/addons/sourcemod/scripting/movementapi/hooks.sp @@ -57,6 +57,7 @@ float gF_PostAAVelocity[MAXPLAYERS + 1][3]; bool gB_OldWalkMoved[MAXPLAYERS + 1]; int gI_CollisionCount[MAXPLAYERS + 1]; +static int gI_PreMoveTouchCount[MAXPLAYERS + 1]; float gF_TraceStartOrigin[MAXPLAYERS + 1][MAX_BUMPS][3]; float gF_TraceEndOrigin[MAXPLAYERS + 1][MAX_BUMPS][3]; @@ -597,6 +598,7 @@ public MRESReturn DHooks_OnTryPlayerMove_Pre(Address pThis, DHookReturn hReturn, gF_TraceNormal[client][i] = NULL_VECTOR; } gI_CollisionCount[client] = 0; + gI_PreMoveTouchCount[client] = GetTouchListCount(); gB_InTryPlayerMove[client] = true; gB_SeededFirstTrace[client] = false; @@ -697,9 +699,16 @@ public MRESReturn DHooks_OnTracePlayerBBox_Post(Address pThis, DHookParam hParam return MRES_Ignored; } +static int GetTouchListCount() +{ + return LoadFromAddress(moveHelperAddr + view_as
(8) + view_as
(12), NumberType_Int32); +} + +// The touch list is only reset once per tick, so earlier entries are from a previous TryPlayerMove. static void ReadTouchListCollisions(int client) { - int touchCount = LoadFromAddress(moveHelperAddr + view_as
(8) + view_as
(12), NumberType_Int32); + int first = gI_PreMoveTouchCount[client]; + int touchCount = GetTouchListCount() - first; if (touchCount > MAX_BUMPS) { touchCount = MAX_BUMPS; @@ -712,7 +721,7 @@ static void ReadTouchListCollisions(int client) Address elements = LoadFromAddress(moveHelperAddr + view_as
(8) + view_as
(16), NumberType_Int32); for (int i = 0; i < touchCount; i++) { - Trace trace = Trace(elements + view_as
(i * 96) + view_as
(12)); + Trace trace = Trace(elements + view_as
((first + i) * 96) + view_as
(12)); trace.startpos.ToArray(gF_TraceStartOrigin[client][i]); trace.endpos.ToArray(gF_TraceEndOrigin[client][i]); trace.plane.normal.ToArray(gF_TraceNormal[client][i]); From 6b2543a7228f571f579f40ec7b1bf7980d8e3a25 Mon Sep 17 00:00:00 2001 From: Dots Date: Mon, 12 Oct 2026 02:22:11 +0300 Subject: [PATCH 3/4] Keep the plugin loaded when the touch list fallback address is missing --- addons/sourcemod/scripting/movementapi/hooks.sp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/addons/sourcemod/scripting/movementapi/hooks.sp b/addons/sourcemod/scripting/movementapi/hooks.sp index c126445..e46a0ac 100644 --- a/addons/sourcemod/scripting/movementapi/hooks.sp +++ b/addons/sourcemod/scripting/movementapi/hooks.sp @@ -78,7 +78,7 @@ void HookGameMovementFunctions() moveHelperAddr = GameConfGetAddress(gH_GameData, "sm_pSingleton"); if (!moveHelperAddr) { - SetFailState("Failed to find IMoveHelper::sm_pSingleton."); + LogError("Failed to find IMoveHelper::sm_pSingleton, the touch list fallback is disabled."); } H_OnTracePlayerBBox = DynamicHook.FromConf(gH_GameData, "CGameMovement::TracePlayerBBox"); @@ -701,6 +701,10 @@ public MRESReturn DHooks_OnTracePlayerBBox_Post(Address pThis, DHookParam hParam static int GetTouchListCount() { + if (!moveHelperAddr) + { + return 0; + } return LoadFromAddress(moveHelperAddr + view_as
(8) + view_as
(12), NumberType_Int32); } @@ -717,6 +721,10 @@ static void ReadTouchListCollisions(int client) { touchCount = 0; } + if (touchCount == 0) + { + return; + } Address elements = LoadFromAddress(moveHelperAddr + view_as
(8) + view_as
(16), NumberType_Int32); for (int i = 0; i < touchCount; i++) From ee903bbc655fbfb9131fb1b98731eb1fcb5571ac Mon Sep 17 00:00:00 2001 From: Dots Date: Mon, 12 Oct 2026 02:22:12 +0300 Subject: [PATCH 4/4] Fail on load when the GetPlayerMaxSpeed SDKCall cannot be prepared --- addons/sourcemod/scripting/movementapi.sp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/addons/sourcemod/scripting/movementapi.sp b/addons/sourcemod/scripting/movementapi.sp index 7929127..72b479a 100644 --- a/addons/sourcemod/scripting/movementapi.sp +++ b/addons/sourcemod/scripting/movementapi.sp @@ -146,6 +146,10 @@ static void PrepSDKCalls() PrepSDKCall_SetFromConf(gH_GameData, SDKConf_Virtual, "GetPlayerMaxSpeed"); PrepSDKCall_SetReturnInfo(SDKType_Float, SDKPass_ByValue); gH_GetMaxSpeed = EndPrepSDKCall(); + if (!gH_GetMaxSpeed) + { + SetFailState("Failed to prepare GetPlayerMaxSpeed SDKCall."); + } } static void ResetClientData(int client)