Skip to content
This repository has been archived by the owner on Mar 5, 2024. It is now read-only.

Commit

Permalink
Added a user-configurable delay before we believe players are ready t…
Browse files Browse the repository at this point in the history
…o vote. This helps with new lobbies where everyone is connecting and a cheater is on your team. See #122.
  • Loading branch information
PazerOP committed Jul 28, 2020
1 parent dcdf29a commit 1a0e9f0
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 45 deletions.
18 changes: 11 additions & 7 deletions tf2_bot_detector/Config/Settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,17 +173,20 @@ void Settings::LoadFile()

if (auto found = json.find("general"); found != json.end())
{
constexpr GeneralSettings DEFAULTS;

try_get_to_defaulted(*found, m_LocalSteamIDOverride, "local_steamid_override");
try_get_to_defaulted(*found, m_SleepWhenUnfocused, "sleep_when_unfocused");
try_get_to_defaulted(*found, m_AutoTempMute, "auto_temp_mute");
try_get_to_defaulted(*found, m_AutoTempMute, "auto_temp_mute", DEFAULTS.m_AutoTempMute);
try_get_to_defaulted(*found, m_AllowInternetUsage, "allow_internet_usage");
try_get_to_defaulted(*found, m_ProgramUpdateCheckMode, "program_update_check_mode");
try_get_to_defaulted(*found, m_ProgramUpdateCheckMode, "program_update_check_mode", DEFAULTS.m_ProgramUpdateCheckMode);
try_get_to_defaulted(*found, m_SteamAPIKey, "steam_api_key");
try_get_to_defaulted(*found, m_AutoLaunchTF2, "auto_launch_tf2");
try_get_to_defaulted(*found, m_AutoChatWarnings, "auto_chat_warnings");
try_get_to_defaulted(*found, m_AutoChatWarningsConnecting, "auto_chat_warnings_connecting");
try_get_to_defaulted(*found, m_AutoVotekick, "auto_votekick");
try_get_to_defaulted(*found, m_AutoMark, "auto_mark");
try_get_to_defaulted(*found, m_AutoLaunchTF2, "auto_launch_tf2", DEFAULTS.m_AutoLaunchTF2);
try_get_to_defaulted(*found, m_AutoChatWarnings, "auto_chat_warnings", DEFAULTS.m_AutoChatWarnings);
try_get_to_defaulted(*found, m_AutoChatWarningsConnecting, "auto_chat_warnings_connecting", DEFAULTS.m_AutoChatWarningsConnecting);
try_get_to_defaulted(*found, m_AutoVotekick, "auto_votekick", DEFAULTS.m_AutoVotekick);
try_get_to_defaulted(*found, m_AutoVotekickDelay, "auto_votekick_delay", DEFAULTS.m_AutoVotekickDelay);
try_get_to_defaulted(*found, m_AutoMark, "auto_mark", DEFAULTS.m_AutoMark);

if (auto foundDir = found->find("steam_dir_override"); foundDir != found->end())
m_SteamDirOverride = foundDir->get<std::string_view>();
Expand Down Expand Up @@ -219,6 +222,7 @@ bool Settings::SaveFile() const
{ "auto_chat_warnings", m_AutoChatWarnings },
{ "auto_chat_warnings_connecting", m_AutoChatWarningsConnecting },
{ "auto_votekick", m_AutoVotekick },
{ "auto_votekick_delay", m_AutoVotekickDelay },
{ "auto_mark", m_AutoMark },
}
},
Expand Down
31 changes: 19 additions & 12 deletions tf2_bot_detector/Config/Settings.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "ChatWrappers.h"
#include "Clock.h"
#include "Networking/HTTPClient.h"
#include "SteamID.h"

Expand Down Expand Up @@ -49,7 +50,24 @@ namespace tf2_bot_detector
std::string CreateProfileURL(const SteamID& id) const;
};

class Settings final : public AutoDetectedSettings
struct GeneralSettings
{
bool m_AutoChatWarnings = true;
bool m_AutoChatWarningsConnecting = false;
bool m_AutoVotekick = true;
float m_AutoVotekickDelay = 15;
bool m_AutoMark = true;

bool m_SleepWhenUnfocused = true;
bool m_AutoTempMute = true;
bool m_AutoLaunchTF2 = false;

ProgramUpdateCheckMode m_ProgramUpdateCheckMode = ProgramUpdateCheckMode::Unknown;

constexpr auto GetAutoVotekickDelay() const { return std::chrono::duration<float>(m_AutoVotekickDelay); }
};

class Settings final : public AutoDetectedSettings, public GeneralSettings
{
public:
Settings();
Expand All @@ -71,21 +89,10 @@ namespace tf2_bot_detector

} m_Unsaved;

bool m_AutoChatWarnings = true;
bool m_AutoChatWarningsConnecting = false;
bool m_AutoVotekick = true;
bool m_AutoMark = true;

bool m_SleepWhenUnfocused = true;
bool m_AutoTempMute = true;
bool m_AutoLaunchTF2 = false;

std::string m_SteamAPIKey;

std::optional<bool> m_AllowInternetUsage;
const HTTPClient* GetHTTPClient() const;
ProgramUpdateCheckMode m_ProgramUpdateCheckMode = ProgramUpdateCheckMode::Unknown;
float m_FriendlyVotekickDelay = 15;

std::vector<GotoProfileSite> m_GotoProfileSites;

Expand Down
3 changes: 3 additions & 0 deletions tf2_bot_detector/IPlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ namespace tf2_bot_detector
virtual time_point_t GetLastStatusUpdateTime() const = 0;
duration_t GetTimeSinceLastStatusUpdate() const;

// The time that this player has been in the "active" state.
virtual duration_t GetActiveTime() const = 0;

operator SteamID() const { return GetSteamID(); }

template<typename T> inline T* GetData()
Expand Down
91 changes: 67 additions & 24 deletions tf2_bot_detector/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,63 @@ void MainWindow::OnDrawColorPickers(const char* id, const std::initializer_list<
});
}

template<bool draw>
static bool OnDrawPlayerTooltipImpl(IPlayer& player, TeamShareResult teamShareResult,
const PlayerMarks& playerAttribs)
{
ImGuiDesktop::ScopeGuards::StyleColor textColor(ImGuiCol_Text, { 1, 1, 1, 1 }, draw);

bool contentDrawn = false;

#ifdef _DEBUG
{
if constexpr (draw)
ImGui::Text("Active time: %1.0fs", to_seconds(player.GetActiveTime()));

contentDrawn = true;
}
#endif

if (teamShareResult != TeamShareResult::SameTeams)
{
if constexpr (draw)
{
auto kills = player.GetScores().m_LocalKills;
auto deaths = player.GetScores().m_LocalDeaths;
//ImGui::Text("Your Thirst: %1.0f%%", kills == 0 ? float(deaths) * 100 : float(deaths) / kills * 100);
ImGui::Text("Their Thirst: %1.0f%%", deaths == 0 ? float(kills) * 100 : float(kills) / deaths * 100);
}

contentDrawn = true;
}

if (playerAttribs)
{
if constexpr (draw)
{
if (contentDrawn)
ImGui::NewLine();

ImGui::TextUnformatted("Player "s << player << " marked in playerlist(s):" << playerAttribs);
}

contentDrawn = true;
}

return contentDrawn;
}

void MainWindow::OnDrawPlayerTooltip(IPlayer& player, TeamShareResult teamShareResult,
const PlayerMarks& playerAttribs)
{
if (OnDrawPlayerTooltipImpl<false>(player, teamShareResult, playerAttribs))
{
ImGui::BeginTooltip();
OnDrawPlayerTooltipImpl<true>(player, teamShareResult, playerAttribs);
ImGui::EndTooltip();
}
}

void MainWindow::OnDrawScoreboard()
{
static float frameWidth, contentWidth, windowContentWidth, windowWidth;
Expand Down Expand Up @@ -365,34 +422,12 @@ void MainWindow::OnDrawScoreboard()
ImGuiDesktop::ScopeGuards::StyleColor styleColorScopeActive(ImGuiCol_HeaderActive, bgColor);
ImGui::Selectable(buf, true, ImGuiSelectableFlags_SpanAllColumns);

if (ImGui::IsItemHovered())
OnDrawPlayerTooltip(player, teamShareResult, playerAttribs);
if ((player.GetSteamID() != m_Settings.GetLocalSteamID()) &&
ImGui::IsItemHovered() &&
(playerAttribs || (teamShareResult != TeamShareResult::SameTeams)))
{
ImGuiDesktop::ScopeGuards::StyleColor textColor(ImGuiCol_Text, { 1, 1, 1, 1 });

ImGui::BeginTooltip();

bool contentDrawn = false;
if (teamShareResult != TeamShareResult::SameTeams)
{
auto kills = player.GetScores().m_LocalKills;
auto deaths = player.GetScores().m_LocalDeaths;
//ImGui::Text("Your Thirst: %1.0f%%", kills == 0 ? float(deaths) * 100 : float(deaths) / kills * 100);
ImGui::Text("Their Thirst: %1.0f%%", deaths == 0 ? float(kills) * 100 : float(kills) / deaths * 100);
contentDrawn = true;
}

if (playerAttribs)
{
if (contentDrawn)
ImGui::NewLine();

ImGui::TextUnformatted("Player "s << player << " marked in playerlist(s):" << playerAttribs);
contentDrawn = true;
}

ImGui::EndTooltip();
}

ImGui::NextColumn();
Expand Down Expand Up @@ -594,6 +629,14 @@ void MainWindow::OnDrawSettingsPopup()
ImGui::SetHoverTooltip("Automatically, temporarily mute ingame chat messages if we think someone else in the server is running the tool.");
}

// Auto votekick delay
{
if (ImGui::SliderFloat("Auto votekick delay", &m_Settings.m_AutoVotekickDelay, 0, 30, "%1.1f seconds"))
m_Settings.SaveFile();
ImGui::SetHoverTooltip("Delay between a player being registered as fully connected and us expecting them to be ready to vote on an issue.\n\n"
"This is needed because players can't vote until they have joined a team and picked a class. If we call a vote before enough people are ready, it might fail.");
}

// Send warnings for connecting cheaters
{
if (ImGui::Checkbox("Chat message warnings for connecting cheaters", &m_Settings.m_AutoChatWarningsConnecting))
Expand Down
1 change: 1 addition & 0 deletions tf2_bot_detector/MainWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ namespace tf2_bot_detector
void OnDrawScoreboardContextMenu(IPlayer& player);
void OnDrawChat();
void OnDrawServerStats();
void OnDrawPlayerTooltip(IPlayer& player, TeamShareResult teamShareResult, const PlayerMarks& playerAttribs);

struct ColorPicker
{
Expand Down
5 changes: 3 additions & 2 deletions tf2_bot_detector/ModeratorLogic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ void ModeratorLogic::HandleFriendlyCheaters(uint8_t friendlyPlayerCount, uint8_t
if (friendlyCheaters.empty())
return; // Nothing to do

constexpr float MIN_QUORUM = 0.6f;
constexpr float MIN_QUORUM = 0.61f;
if (auto quorum = float(connectedFriendlyPlayerCount) / friendlyPlayerCount; quorum <= MIN_QUORUM)
{
LogWarning("Impossible to pass a successful votekick against "s << friendlyCheaters.size()
Expand Down Expand Up @@ -410,7 +410,8 @@ void ModeratorLogic::ProcessPlayerActions()
{
if (isPlayerConnected)
{
connectedFriendlyPlayers++;
if (player.GetActiveTime() > m_Settings->GetAutoVotekickDelay())
connectedFriendlyPlayers++;

if (isCheater)
friendlyCheaters.push_back({ player, isCheater });
Expand Down
11 changes: 11 additions & 0 deletions tf2_bot_detector/WorldState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -623,8 +623,19 @@ duration_t WorldState::PlayerExtraData::GetConnectedTime() const
return result;
}

duration_t WorldState::PlayerExtraData::GetActiveTime() const
{
if (m_Status.m_State != PlayerStatusState::Active)
return 0s;

return m_LastStatusUpdateTime - m_LastStatusActiveBegin;
}

void WorldState::PlayerExtraData::SetStatus(PlayerStatus status, time_point_t timestamp)
{
if (m_Status.m_State != PlayerStatusState::Active && status.m_State == PlayerStatusState::Active)
m_LastStatusActiveBegin = timestamp;

m_Status = std::move(status);
m_PlayerNameSafe = CollapseNewlines(m_Status.m_Name);
m_LastStatusUpdateTime = m_LastPingUpdateTime = timestamp;
Expand Down
3 changes: 3 additions & 0 deletions tf2_bot_detector/WorldState.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ namespace tf2_bot_detector
uint16_t GetPing() const override { return m_Status.m_Ping; }
time_point_t GetLastStatusUpdateTime() const override { return m_LastStatusUpdateTime; }
const SteamAPI::PlayerSummary* GetPlayerSummary() const override { return m_PlayerSummary ? &*m_PlayerSummary : nullptr; }
duration_t GetActiveTime() const override;

WorldState* m_World{};
PlayerScores m_Scores{};
Expand All @@ -134,6 +135,8 @@ namespace tf2_bot_detector
PlayerStatus m_Status{};
std::string m_PlayerNameSafe;

time_point_t m_LastStatusActiveBegin{};

time_point_t m_LastStatusUpdateTime{};
time_point_t m_LastPingUpdateTime{};
};
Expand Down

0 comments on commit 1a0e9f0

Please sign in to comment.