diff --git a/include/progs.h b/include/progs.h index 36846f9f..8dc52756 100644 --- a/include/progs.h +++ b/include/progs.h @@ -970,6 +970,19 @@ typedef struct gedict_s float fIllegalFPSWarnings; // ILLEGALFPS] +// SOCD detectioin + float fStrafeChangeCount; + float fFramePerfectStrafeChangeCount; + int socdDetected; + int socdChecksCount; + float fLastSideMoveSpeed; + int matchStrafeChangeCount; + int matchPerfectStrafeCount; + int totalStrafeChangeCount; + int totalPerfectStrafeCount; + int nullStrafeCount; +// SOCD + float shownick_time; // used to force centerprint is off at desired time clientType_t ct; // client type for client edicts // { timing diff --git a/src/client.c b/src/client.c index bcfd344b..f9da3756 100644 --- a/src/client.c +++ b/src/client.c @@ -1791,6 +1791,18 @@ void ClientConnect() SendIntermissionToClient(); } +// SOCD + self->socdChecksCount = 0; + self->socdDetected = 0; + self->fStrafeChangeCount = 0; + self->fFramePerfectStrafeChangeCount = 0; + self->fLastSideMoveSpeed = 0; + self->matchStrafeChangeCount = 0; + self->matchPerfectStrafeCount = 0; + self->totalStrafeChangeCount = 0; + self->totalPerfectStrafeCount = 0; + self->nullStrafeCount = 0; + // ILLEGALFPS[ // Zibbo's frametime checking code @@ -3693,6 +3705,57 @@ void PlayerPreThink() } #endif +// SOCD detection + + float fSideMoveSpeed = self->movement[1]; + + if ((fSideMoveSpeed != 0) && (fSideMoveSpeed != self->fLastSideMoveSpeed) && (self->nullStrafeCount < 3)) //strafechange + { + self->fStrafeChangeCount += 1; + self->totalStrafeChangeCount += 1; + if (match_in_progress) + self->matchStrafeChangeCount += 1; + + if ((fSideMoveSpeed != 0) && (self->fLastSideMoveSpeed != 0)) + { + self->fFramePerfectStrafeChangeCount += 1; + self->totalPerfectStrafeCount += 1; + if (match_in_progress) + self->matchPerfectStrafeCount += 1; + } + + self->nullStrafeCount = 0; + } + else + { + if (0 == fSideMoveSpeed) + self->nullStrafeCount += 1; + else + self->nullStrafeCount = 0; + } + + self->fLastSideMoveSpeed = fSideMoveSpeed; + + if (self->fStrafeChangeCount >= 25) + { + if (self->fFramePerfectStrafeChangeCount / self->fStrafeChangeCount >= 0.75) + { + int k_allow_socd_warning = cvar("k_allow_socd_warning"); + + self->socdDetected += 1; + if ((!match_in_progress) && (!self->isBot) && k_allow_socd_warning && (self->ct == ctPlayer)) + { + G_bprint(PRINT_HIGH, + "Warning! %s: Movement assistance detected. Please disable iDrive or keyboard strafe assistance features.\n", + self->netname); + } + } + + self->socdChecksCount += 1; + self->fStrafeChangeCount = 0; + self->fFramePerfectStrafeChangeCount = 0; + } + // ILLEGALFPS[ self->fAverageFrameTime += g_globalvars.frametime; diff --git a/src/commands.c b/src/commands.c index 4a7648b2..b877acc3 100644 --- a/src/commands.c +++ b/src/commands.c @@ -8178,6 +8178,7 @@ void fcheck() { char arg_x[1024]; int i; + gedict_t* p; if (match_in_progress) { @@ -8203,16 +8204,41 @@ void fcheck() if (!is_real_adm(self)) { - if (strneq(arg_x, "f_version") && strneq(arg_x, "f_modified") && strneq(arg_x, "f_server")) + if (strneq(arg_x, "f_version") && strneq(arg_x, "f_modified") && strneq(arg_x, "f_server") && strneq(arg_x, "f_movement")) { G_sprint(self, 2, "You are not allowed to check \020%s\021\n" - "available checks are: f_version, f_modified and f_server\n", + "available checks are: f_version, f_modified, f_server and f_movement\n", arg_x); return; } } + if (streq(arg_x, "f_movement")) + { + G_bprint(2, "%s is checking \020%s\021\n", self->netname, arg_x); + + for (p = world; (p = find_client(p));) + { + if ((p->ct == ctPlayer) && (!p->isBot)) + { + if (p->socdDetected > 0) + { + G_bprint(2, "%s: %s:%.1f%% (%d/%d). SOCD movement assistance detected!\n", p->netname, redtext("Perfect strafes"), + p->totalStrafeChangeCount > 0 ? 100.0 * p->totalPerfectStrafeCount / p->totalStrafeChangeCount : 0.0, + p->totalPerfectStrafeCount, p->totalStrafeChangeCount); + } + else + { + G_bprint(2, "%s: %s:%.1f%% (%d/%d)\n", p->netname, redtext("Perfect strafes"), + p->totalStrafeChangeCount > 0 ? 100.0 * p->totalPerfectStrafeCount / p->totalStrafeChangeCount : 0.0, + p->totalPerfectStrafeCount, p->totalStrafeChangeCount); + } + } + } + return; + } + for (i = 1; i <= MAX_CLIENTS; i++) { if (g_edicts[i].f_checkbuf) diff --git a/src/stats.c b/src/stats.c index 94f57005..fbc018d6 100644 --- a/src/stats.c +++ b/src/stats.c @@ -763,6 +763,16 @@ void OnePlayerStats(gedict_t *p, int tp) p->ps.vel_frames > 0 ? p->ps.velocity_sum / p->ps.vel_frames : 0.); } + // movement + if (!p->isBot) + { + G_bprint(2, "%s: %s:%.1f%% (%d/%d) %s:%d/%d\n", redtext("Movement"), redtext("Perfect strafes"), + p->matchStrafeChangeCount > 0 ? 100.0 * p->matchPerfectStrafeCount / p->matchStrafeChangeCount : 0.0, + p->matchPerfectStrafeCount, p->matchStrafeChangeCount, redtext("SOCD detections"), + p->socdDetected, p->socdChecksCount); + } + + // armors + megahealths if (!lgc_enabled()) { diff --git a/src/world.c b/src/world.c index ea77870e..6201293e 100644 --- a/src/world.c +++ b/src/world.c @@ -1001,6 +1001,8 @@ void FirstFrame() RegisterCvar("k_teamoverlay"); // q3 like team overlay + RegisterCvar("k_allow_socd_warning"); // socd + // { SP RegisterCvarEx("k_monster_spawn_time", "20"); // }