Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only increment timer and record replays if movement processing happens, fix abuse with zones and low fps/high packet loss #371

Open
wants to merge 5 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions addons/sourcemod/gamedata/gokz-core.games.txt
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@
"linux" "222"
}
}
"Signatures"
{
"PhysicsCheckForEntityUntouch"
{
"windows" "\x55\x8B\xEC\x83\xEC\x08\x56\x8B\xF1\x8B\x86\xD0\x00\x00\x00"
"linux" "\x55\x89\xE5\x57\x56\x53\x83\xEC\x24\x8B\x75\x2A\x6A\x01\x56"
}
}
}

"cstrike"
Expand Down
21 changes: 16 additions & 5 deletions addons/sourcemod/scripting/gokz-core.sp
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,7 @@ public void OnPlayerRunCmdPost(int client, int buttons, int impulse, const float
{
return;
}

OnPlayerRunCmdPost_VirtualButtons(client, buttons, cmdnum); // Emulate buttons first
OnPlayerRunCmdPost_Timer(client); // This should be first after emulating buttons
OnPlayerRunCmdPost_VirtualButtons(client, cmdnum);
OnPlayerRunCmdPost_ValidJump(client);
UpdateTrackingVariables(client, cmdnum, buttons); // This should be last
}
Expand Down Expand Up @@ -264,14 +262,25 @@ public MRESReturn DHooks_OnSetModel(int client, Handle params)
return MRES_Handled;
}

public void OnCSPlayerSpawnPost(int client)
public void Hook_PlayerSpawnPost(int client)
{
if (GetEntPropEnt(client, Prop_Send, "m_hGroundEntity") == -1)
{
SetEntityFlags(client, GetEntityFlags(client) & ~FL_ONGROUND);
}
}

public void Hook_PlayerPostThink(int client)
{
Hook_PlayerPostThink_Triggerfix(client);
}

public void Hook_PlayerPostThinkPost(int client)
{
Hook_PlayerPostThinkPost_VirtualButtons(client);
Hook_PlayerPostThinkPost_Timer(client); // This should be first after emulating buttons
}

public void Movement_OnChangeMovetype(int client, MoveType oldMovetype, MoveType newMovetype)
{
OnChangeMovetype_Timer(client, newMovetype);
Expand Down Expand Up @@ -510,7 +519,9 @@ static void HookClientEvents(int client)
{
DHookEntity(gH_DHooks_OnTeleport, true, client);
DHookEntity(gH_DHooks_SetModel, true, client);
SDKHook(client, SDKHook_SpawnPost, OnCSPlayerSpawnPost);
SDKHook(client, SDKHook_PostThink, Hook_PlayerPostThink);
SDKHook(client, SDKHook_PostThinkPost, Hook_PlayerPostThinkPost);
SDKHook(client, SDKHook_SpawnPost, Hook_PlayerSpawnPost);
}

static void UpdateTrackingVariables(int client, int cmdnum, int buttons)
Expand Down
2 changes: 1 addition & 1 deletion addons/sourcemod/scripting/gokz-core/timer/timer.sp
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ void OnClientPutInServer_Timer(int client)
lastStartMode[client] = MODE_COUNT; // So it won't equal any mode
}

void OnPlayerRunCmdPost_Timer(int client)
void Hook_PlayerPostThinkPost_Timer(int client)
{
if (IsPlayerAlive(client) && GetTimerRunning(client) && !GetPaused(client))
{
Expand Down
8 changes: 6 additions & 2 deletions addons/sourcemod/scripting/gokz-core/timer/virtual_buttons.sp
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,13 @@ void OnEndButtonPress_VirtualButtons(int client, int course)
}
}

void OnPlayerRunCmdPost_VirtualButtons(int client, int buttons, int cmdnum)
void Hook_PlayerPostThinkPost_VirtualButtons(int client)
{
CheckForAndHandleUsage(client, GetClientButtons(client));
}

void OnPlayerRunCmdPost_VirtualButtons(int client, int cmdnum)
{
CheckForAndHandleUsage(client, buttons);
UpdateIndicators(client, cmdnum);
}

Expand Down
152 changes: 88 additions & 64 deletions addons/sourcemod/scripting/gokz-core/triggerfix.sp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
// Engine constants, NOT settings (do not change)
#define LAND_HEIGHT 2.0 // Maximum height above ground at which you can "land"
#define MIN_STANDABLE_ZNRM 0.7 // Minimum surface normal Z component of a walkable surface
#define EFL_CHECK_UNTOUCH 1<<24

static int processMovementTicks[MAXPLAYERS+1];
static float playerFrameTime[MAXPLAYERS+1];
Expand All @@ -21,6 +22,7 @@ static float jumpBugOrigin[MAXPLAYERS + 1][3];

static ConVar cvGravity;

static Handle physicsCheckForEntityUntouch;
static Handle acceptInputHookPre;
static Handle processMovementHookPre;
static Address serverGameEnts;
Expand All @@ -43,6 +45,15 @@ public void OnPluginStart_Triggerfix()
SetFailState("Failed to load gokz-core gamedata");
}

// EndTouch Fix
// Thanks rumour and mev
StartPrepSDKCall(SDKCall_Entity);
if(!PrepSDKCall_SetFromConf(gamedataConf, SDKConf_Signature, "PhysicsCheckForEntityUntouch"))
{
SetFailState("Failed to get PhysicsCheckForEntityUntouch");
}

physicsCheckForEntityUntouch = EndPrepSDKCall();
// PassesTriggerFilters
StartPrepSDKCall(SDKCall_Entity);
if (!PrepSDKCall_SetFromConf(gamedataConf, SDKConf_Virtual, "CBaseTrigger::PassesTriggerFilters"))
Expand Down Expand Up @@ -177,7 +188,6 @@ public void OnClientConnected_Triggerfix(int client)

public void OnClientPutInServer_Triggerfix(int client)
{
SDKHook(client, SDKHook_PostThink, Hook_PlayerPostThink);
DHookEntity(acceptInputHookPre, false, client);
}

Expand Down Expand Up @@ -415,87 +425,95 @@ static bool DoTriggerjumpFix(int client, const float landingPoint[3], const floa
}

// PostThink works a little better than a ProcessMovement post hook because we need to wait for ProcessImpacts (trigger activation)
static void Hook_PlayerPostThink(int client)
void Hook_PlayerPostThink_Triggerfix(int client)
{
if (!IsPlayerAlive(client)
|| GetEntityMoveType(client) != MOVETYPE_WALK
|| CheckWater(client))
if (!IsPlayerAlive(client))
{
return;
}

bool landed = (GetEntPropEnt(client, Prop_Data, "m_hGroundEntity") != -1
&& lastGroundEnt[client] == -1)
|| jumpBugged[client];

float landingMins[3], landingMaxs[3], landingPoint[3];

// Get info about the ground we landed on (if we need to do landing fixes).
if (landed)

// Trigger jump fix
if (GetEntityMoveType(client) == MOVETYPE_WALK && !CheckWater(client))
{
float origin[3], nrm[3], velocity[3];
GetEntPropVector(client, Prop_Data, "m_vecAbsOrigin", origin);
GetEntPropVector(client, Prop_Data, "m_vecVelocity", velocity);
bool landed = (GetEntPropEnt(client, Prop_Data, "m_hGroundEntity") != -1
&& lastGroundEnt[client] == -1)
|| jumpBugged[client];

if (jumpBugged[client])
{
origin = jumpBugOrigin[client];
}
float landingMins[3], landingMaxs[3], landingPoint[3];

GetEntPropVector(client, Prop_Data, "m_vecMins", landingMins);
GetEntPropVector(client, Prop_Data, "m_vecMaxs", landingMaxs);

float originBelow[3];
originBelow[0] = origin[0];
originBelow[1] = origin[1];
originBelow[2] = origin[2] - LAND_HEIGHT;

TR_TraceHullFilter(origin, originBelow, landingMins, landingMaxs, MASK_PLAYERSOLID, PlayerFilter);

if (!TR_DidHit())
{
// This should never happen, since we know we are on the ground.
landed = false;
}
else
// Get info about the ground we landed on (if we need to do landing fixes).
if (landed)
{
TR_GetPlaneNormal(null, nrm);
float origin[3], nrm[3], velocity[3];
GetEntPropVector(client, Prop_Data, "m_vecAbsOrigin", origin);
GetEntPropVector(client, Prop_Data, "m_vecVelocity", velocity);

if (jumpBugged[client])
{
origin = jumpBugOrigin[client];
}

GetEntPropVector(client, Prop_Data, "m_vecMins", landingMins);
GetEntPropVector(client, Prop_Data, "m_vecMaxs", landingMaxs);

if (nrm[2] < MIN_STANDABLE_ZNRM)
float originBelow[3];
originBelow[0] = origin[0];
originBelow[1] = origin[1];
originBelow[2] = origin[2] - LAND_HEIGHT;

TR_TraceHullFilter(origin, originBelow, landingMins, landingMaxs, MASK_PLAYERSOLID, PlayerFilter);

if (!TR_DidHit())
{
// This is rare, and how the incline fix should behave isn't entirely clear because maybe we should
// collide with multiple faces at once in this case, but let's just get the ground we officially
// landed on and use that for our ground normal.
// This should never happen, since we know we are on the ground.
landed = false;
}
else
{
TR_GetPlaneNormal(null, nrm);

// landingMins and landingMaxs will contain the final values used to find the ground after returning.
if (TracePlayerBBoxForGround(origin, originBelow, landingMins, landingMaxs))
{
TR_GetPlaneNormal(null, nrm);
}
else
if (nrm[2] < MIN_STANDABLE_ZNRM)
{
// This should also never happen.
landed = false;
// This is rare, and how the incline fix should behave isn't entirely clear because maybe we should
// collide with multiple faces at once in this case, but let's just get the ground we officially
// landed on and use that for our ground normal.

// landingMins and landingMaxs will contain the final values used to find the ground after returning.
if (TracePlayerBBoxForGround(origin, originBelow, landingMins, landingMaxs))
{
TR_GetPlaneNormal(null, nrm);
}
else
{
// This should also never happen.
landed = false;
}
}

TR_GetEndPosition(landingPoint);
}
}

// reset it here because we don't need it again
jumpBugged[client] = false;

// Must use TR_DidHit because if the unduck origin is closer than 0.03125 units from the ground,
// the trace fraction would return 0.0.
if (landed && TR_DidHit())
{
DoTriggerjumpFix(client, landingPoint, landingMins, landingMaxs);
// Check if a trigger we just touched put us in the air (probably due to a teleport).
if (GetEntityFlags(client) & FL_ONGROUND == 0)
{
landed = false;
}

TR_GetEndPosition(landingPoint);
}
}

// reset it here because we don't need it again
jumpBugged[client] = false;

// Must use TR_DidHit because if the unduck origin is closer than 0.03125 units from the ground,
// the trace fraction would return 0.0.
if (landed && TR_DidHit())
// End touch fix
if (GetCheckUntouch(client))
{
DoTriggerjumpFix(client, landingPoint, landingMins, landingMaxs);
// Check if a trigger we just touched put us in the air (probably due to a teleport).
if (GetEntityFlags(client) & FL_ONGROUND == 0)
{
landed = false;
}
SDKCall(physicsCheckForEntityUntouch, client);
}
}

Expand Down Expand Up @@ -620,3 +638,9 @@ static bool TracePlayerBBoxForGround(const float origin[3], const float originBe

return false;
}

static bool GetCheckUntouch(int client)
{
int flags = GetEntProp(client, Prop_Data, "m_iEFlags");
return (flags & EFL_CHECK_UNTOUCH) != 0;
}
20 changes: 16 additions & 4 deletions addons/sourcemod/scripting/gokz-replays.sp
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ public void OnClientPutInServer(int client)
{
OnClientPutInServer_Playback(client);
OnClientPutInServer_Recording(client);
HookClientEvents(client);
}

public void OnClientAuthorized(int client, const char[] auth)
Expand All @@ -223,12 +224,18 @@ public void OnClientDisconnect(int client)

public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3], float angles[3], int &weapon, int &subtype, int &cmdnum, int &tickcount, int &seed, int mouse[2])
{
if (!IsFakeClient(client))
OnPlayerRunCmd_Recording(client);
if (IsFakeClient(client))
{
return Plugin_Continue;
OnPlayerRunCmd_Playback(client, buttons, vel, angles);
return Plugin_Changed;
}
OnPlayerRunCmd_Playback(client, buttons, vel, angles);
return Plugin_Changed;
return Plugin_Continue;
}

public void Hook_PlayerPostThinkPost(int client)
{
Hook_PlayerPostThinkPost_Recording(client);
}

public void OnPlayerRunCmdPost(int client, int buttons, int impulse, const float vel[3], const float angles[3], int weapon, int subtype, int cmdnum, int tickcount, int seed, const int mouse[2])
Expand Down Expand Up @@ -336,6 +343,11 @@ static void HookEvents()
delete gameData;
}

static void HookClientEvents(int client)
{
SDKHook(client, SDKHook_PostThinkPost, Hook_PlayerPostThinkPost);
}

static void UpdateCurrentMap()
{
GetCurrentMapDisplayName(gC_CurrentMap, sizeof(gC_CurrentMap));
Expand Down
23 changes: 22 additions & 1 deletion addons/sourcemod/scripting/gokz-replays/recording.sp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ static ArrayList recordedRunData[MAXPLAYERS + 1];
static ArrayList recordedPostRunData[MAXPLAYERS + 1];
static Handle runningRunBreatherTimer[MAXPLAYERS + 1];
static ArrayList runningJumpstatTimers[MAXPLAYERS + 1];
static bool movementProcessed[MAXPLAYERS + 1];

// =====[ EVENTS ]=====

Expand Down Expand Up @@ -97,12 +98,32 @@ void OnClientDisconnect_Recording(int client)
ClearClientRecordingState(client);
}

void OnPlayerRunCmdPost_Recording(int client, int buttons, int tickCount, const float vel[3], const int mouse[2])
void OnPlayerRunCmd_Recording(int client)
{
if (!IsValidClient(client) || IsFakeClient(client) || !IsPlayerAlive(client) || recordingPaused[client])
{
return;
}
movementProcessed[client] = false;
}

void Hook_PlayerPostThinkPost_Recording(int client)
{
if (!IsValidClient(client) || IsFakeClient(client) || !IsPlayerAlive(client) || recordingPaused[client])
{
return;
}
// If movement is processed, then this function will be called.
// We don't record ticks where no movement processing happens.
movementProcessed[client] = true;
}

void OnPlayerRunCmdPost_Recording(int client, int buttons, int tickCount, const float vel[3], const int mouse[2])
{
if (!IsValidClient(client) || IsFakeClient(client) || !IsPlayerAlive(client) || recordingPaused[client] || !movementProcessed[client])
{
return;
}

ReplayTickData tickData;

Expand Down