From f3e73e0cdcfd7bb457752b4bbcbcdaaf9f51ccd3 Mon Sep 17 00:00:00 2001
From: At0mBomba <102352627+At0mBomba@users.noreply.github.com>
Date: Sun, 19 Jul 2026 14:32:03 +0300
Subject: [PATCH] Attempt to fix "banned by Innersloth Anti-Cheat" issues
---
src/Managers/KickCooldownManager.cs | 36 ++++++
src/Modules/AntiCheat/BetterAntiCheat.cs | 10 +-
src/MonoScripts/PlayerInfoDisplay.cs | 2 +-
.../Anticheat/CheckPlayerLevelPatch.cs | 6 +-
.../Gameplay/Player/PlayerJoinAndLeftPatch.cs | 11 +-
.../Gameplay/UI/Settings/GameSettingsPatch.cs | 2 +
src/Resources/Lang/en_US.json | 1 +
src/Utilities/PlayerControlUtils.cs | 120 ++++++++++++++----
src/build/Generated/TranslationStrings.cs | 5 +
9 files changed, 156 insertions(+), 37 deletions(-)
create mode 100644 src/Managers/KickCooldownManager.cs
diff --git a/src/Managers/KickCooldownManager.cs b/src/Managers/KickCooldownManager.cs
new file mode 100644
index 0000000..5092a35
--- /dev/null
+++ b/src/Managers/KickCooldownManager.cs
@@ -0,0 +1,36 @@
+using System.Collections;
+using BepInEx.Unity.IL2CPP.Utils;
+using BetterAmongUs.Patches.Gameplay.UI.Settings;
+using UnityEngine;
+
+namespace BetterAmongUs.Managers;
+
+///
+/// Manages the cooldown period between player kick actions.
+/// This cooldown is necessary to avoid bans by Innersloth anti-cheat.
+///
+internal static class KickCooldownManager
+{
+ private static float CooldownSeconds => BetterGameSettings.KickCooldown.GetFloat();
+
+ private static float _lastTriggerTime;
+
+ internal static bool IsReady() => Time.time - _lastTriggerTime > CooldownSeconds;
+ internal static void Trigger() => _lastTriggerTime = Time.time;
+ private static float TimeToNextAvailableTrigger() => CooldownSeconds - (Time.time - _lastTriggerTime);
+
+ internal static void ScheduleAction(Action action)
+ {
+ AmongUsClient.Instance.StartCoroutine(RunScheduledAction(action));
+ }
+
+ private static IEnumerator RunScheduledAction(Action action)
+ {
+ while (!IsReady())
+ {
+ yield return new WaitForSeconds(TimeToNextAvailableTrigger());
+ }
+ Trigger();
+ action();
+ }
+}
\ No newline at end of file
diff --git a/src/Modules/AntiCheat/BetterAntiCheat.cs b/src/Modules/AntiCheat/BetterAntiCheat.cs
index 92bb9cd..374619e 100644
--- a/src/Modules/AntiCheat/BetterAntiCheat.cs
+++ b/src/Modules/AntiCheat/BetterAntiCheat.cs
@@ -51,31 +51,31 @@ internal static class BetterAntiCheat
{
string reason = TranslationStrings.AntiCheat_Reason_SickoMenuUser.LocalizedString;
string kickMessage = TranslationStrings.AntiCheat_KickMessage.Format(TranslationStrings.AntiCheat_ByAntiCheat, reason);
- player.Kick(true, kickMessage, true);
+ player.TryKick(true, kickMessage, true);
}
else if (BetterDataManager.Files.BetterDataFile.AUMData.Any(info => info.CheckPlayerData(player.Data)))
{
string reason = TranslationStrings.AntiCheat_Reason_AUMUser.LocalizedString;
string kickMessage = TranslationStrings.AntiCheat_KickMessage.Format(TranslationStrings.AntiCheat_ByAntiCheat, reason);
- player.Kick(true, kickMessage, true);
+ player.TryKick(true, kickMessage, true);
}
else if (BetterDataManager.Files.BetterDataFile.KNData.Any(info => info.CheckPlayerData(player.Data)))
{
string reason = TranslationStrings.AntiCheat_Reason_KNUser.LocalizedString;
string kickMessage = TranslationStrings.AntiCheat_KickMessage.Format(TranslationStrings.AntiCheat_ByAntiCheat, reason);
- player.Kick(true, kickMessage, true);
+ player.TryKick(true, kickMessage, true);
}
else if (BetterDataManager.Files.BetterDataFile.MMCData.Any(info => info.CheckPlayerData(player.Data)))
{
string reason = TranslationStrings.AntiCheat_Reason_MMCUser.LocalizedString;
string kickMessage = TranslationStrings.AntiCheat_KickMessage.Format(TranslationStrings.AntiCheat_ByAntiCheat, reason);
- player.Kick(true, kickMessage, true);
+ player.TryKick(true, kickMessage, true);
}
else if (BetterDataManager.Files.BetterDataFile.CheatData.Any(info => info.CheckPlayerData(player.Data)))
{
string reason = TranslationStrings.AntiCheat_Reason_KnownCheater.LocalizedString;
string kickMessage = TranslationStrings.AntiCheat_KickMessage.Format(TranslationStrings.AntiCheat_ByAntiCheat, reason);
- player.Kick(true, kickMessage, true);
+ player.TryKick(true, kickMessage, true);
}
}
}
diff --git a/src/MonoScripts/PlayerInfoDisplay.cs b/src/MonoScripts/PlayerInfoDisplay.cs
index 4432f7d..9c33aac 100644
--- a/src/MonoScripts/PlayerInfoDisplay.cs
+++ b/src/MonoScripts/PlayerInfoDisplay.cs
@@ -277,7 +277,7 @@ internal class PlayerInfoDisplay : MonoBehaviour
if (GameState.IsHost && BetterGameSettings.InvalidFriendCode.GetBool())
{
string kickMessage = TranslationStrings.AntiCheat_KickMessage.Format(TranslationStrings.AntiCheat_ByAntiCheat, TranslationStrings.AntiCheat_Reason_InvalidFriendCode);
- _player.Kick(true, kickMessage, true);
+ _player.TryKick(true, kickMessage, true);
}
}
diff --git a/src/Patches/Gameplay/Anticheat/CheckPlayerLevelPatch.cs b/src/Patches/Gameplay/Anticheat/CheckPlayerLevelPatch.cs
index 42d55a5..e18f9d2 100644
--- a/src/Patches/Gameplay/Anticheat/CheckPlayerLevelPatch.cs
+++ b/src/Patches/Gameplay/Anticheat/CheckPlayerLevelPatch.cs
@@ -1,6 +1,6 @@
-using BetterAmongUs.Modules;
+using BetterAmongUs.Utilities;
+using BetterAmongUs.Modules;
using BetterAmongUs.Patches.Gameplay.UI.Settings;
-using BetterAmongUs.Utilities;
using HarmonyLib;
namespace BetterAmongUs.Patches.Gameplay.Anticheat;
@@ -20,7 +20,7 @@ internal static class CheckPlayerLevelPatch
// Kick players below minimum level
if (!__instance.IsLocalPlayer() && BetterGameSettings.KickLevel.GetBool() && __instance.Data.PlayerLevel < BetterGameSettings.KickLevelBelow.GetInt())
{
- __instance.Kick(setReasonInfo: $" is level {__instance.Data.PlayerLevel}, level must be equal or above {BetterGameSettings.KickLevelBelow.GetInt()} to join");
+ __instance.TryKick(setReasonInfo: $" is level {__instance.Data.PlayerLevel}, level must be equal or above {BetterGameSettings.KickLevelBelow.GetInt()} to join");
}
}
}
diff --git a/src/Patches/Gameplay/Player/PlayerJoinAndLeftPatch.cs b/src/Patches/Gameplay/Player/PlayerJoinAndLeftPatch.cs
index 99b5da5..e3bf117 100644
--- a/src/Patches/Gameplay/Player/PlayerJoinAndLeftPatch.cs
+++ b/src/Patches/Gameplay/Player/PlayerJoinAndLeftPatch.cs
@@ -1,7 +1,9 @@
+using System.Collections;
using BepInEx.Unity.IL2CPP.Utils;
using BetterAmongUs.Data;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Generated;
+using BetterAmongUs.Managers;
using BetterAmongUs.Modules;
using BetterAmongUs.Modules.Support;
using BetterAmongUs.MonoScripts.Extended;
@@ -10,7 +12,6 @@ using BetterAmongUs.Patches.Gameplay.UI.Settings;
using BetterAmongUs.Utilities;
using HarmonyLib;
using InnerNet;
-using System.Collections;
namespace BetterAmongUs.Patches.Gameplay.Player;
@@ -38,6 +39,7 @@ internal static class PlayerJoinAndLeftPatch
{
if (GameState.IsHost)
{
+ KickCooldownManager.Trigger();
__instance.StartCoroutine(CoCheckPlayerOnJoin(data));
}
}
@@ -59,8 +61,7 @@ internal static class PlayerJoinAndLeftPatch
if (player != null)
{
if (TextFileHandler.CompareStringMatch(BetterDataManager.Files.banPlayerListFilePath,
- BAUPlugin.AllPlayerControls.Select(player => player.Data.FriendCode)
- .Concat(BAUPlugin.AllPlayerControls.Select(player => player.GetHashPuid())).ToArray()))
+ [player.Data.FriendCode, player.GetHashPuid()]))
{
player.Kick(true, TranslationStrings.AntiCheat_BanPlayerListMessage.LocalizedString, bypassDataCheck: true);
yield break;
@@ -75,7 +76,7 @@ internal static class PlayerJoinAndLeftPatch
{
if (TextFileHandler.CompareStringFilters(BetterDataManager.Files.banNameListFilePath, [player.Data.PlayerName]))
{
- player?.Kick(true, TranslationStrings.AntiCheat_BanPlayerListMessage.LocalizedString, bypassDataCheck: true);
+ player.Kick(true, TranslationStrings.AntiCheat_BanNameListMessage.LocalizedString, bypassDataCheck: true);
}
}
}
@@ -86,6 +87,8 @@ internal static class PlayerJoinAndLeftPatch
[HarmonyPostfix]
private static void AmongUsClient_OnPlayerLeft_Postfix(ClientData data, DisconnectReasons reason)
{
+ KickCooldownManager.Trigger();
+
// Reclaim favorite color when player leaves in lobby
if (GameState.IsLobby)
{
diff --git a/src/Patches/Gameplay/UI/Settings/GameSettingsPatch.cs b/src/Patches/Gameplay/UI/Settings/GameSettingsPatch.cs
index 05417a2..ab230c1 100644
--- a/src/Patches/Gameplay/UI/Settings/GameSettingsPatch.cs
+++ b/src/Patches/Gameplay/UI/Settings/GameSettingsPatch.cs
@@ -11,6 +11,7 @@ namespace BetterAmongUs.Patches.Gameplay.UI.Settings;
// Custom game settings definitions
internal sealed class BetterGameSettings
{
+ internal static OptionFloatItem? KickCooldown;
internal static OptionStringItem? WhenCheating;
internal static OptionCheckboxItem? InvalidFriendCode;
internal static OptionCheckboxItem? UseBanPlayerList;
@@ -67,6 +68,7 @@ internal static class GameSettingsPatch
if (IsPreload || GameState.IsHost)
{
OptionTitleItem.Create(BetterSettingsTab, TranslationStrings.BetterSetting_TextHeader_HostOnly);
+ BetterGameSettings.KickCooldown = OptionFloatItem.Create(BetterSettingsTab, TranslationStrings.BetterSetting_Setting_KickCooldown, (0f, 3f, 0.1f), 2f, ("", "s"));
BetterGameSettings.WhenCheating = OptionStringItem.Create(BetterSettingsTab, TranslationStrings.BetterSetting_Setting_WhenCheating,
[TranslationStrings.BetterSetting_Setting_WhenCheating_Notify, TranslationStrings.BetterSetting_Setting_WhenCheating_Kick, TranslationStrings.BetterSetting_Setting_WhenCheating_Ban], 2);
BetterGameSettings.InvalidFriendCode = OptionCheckboxItem.Create(BetterSettingsTab, TranslationStrings.BetterSetting_Setting_InvalidFriendCode, true);
diff --git a/src/Resources/Lang/en_US.json b/src/Resources/Lang/en_US.json
index 3042bc2..ec8ec52 100644
--- a/src/Resources/Lang/en_US.json
+++ b/src/Resources/Lang/en_US.json
@@ -79,6 +79,7 @@
"BetterSetting.TextHeader.HostOnly": "Host Only",
"BetterSetting.TextHeader.Detections": "Detections",
+ "BetterSetting.Setting.KickCooldown": "Kick cooldown",
"BetterSetting.Setting.WhenCheating": "When a player is caught cheating",
"BetterSetting.Setting.WhenCheating.Notify": "Notify",
"BetterSetting.Setting.WhenCheating.Kick": "Kick",
diff --git a/src/Utilities/PlayerControlUtils.cs b/src/Utilities/PlayerControlUtils.cs
index 0fc610c..128d3b9 100644
--- a/src/Utilities/PlayerControlUtils.cs
+++ b/src/Utilities/PlayerControlUtils.cs
@@ -1,6 +1,7 @@
using AmongUs.GameOptions;
using BetterAmongUs.Data;
using BetterAmongUs.Generated;
+using BetterAmongUs.Managers;
using BetterAmongUs.Modules;
using BetterAmongUs.MonoScripts.Extended;
using BetterAmongUs.Patches.Gameplay.Player;
@@ -127,42 +128,113 @@ internal static class PlayerControlUtils
return true;
}
+ ///
+ /// Kicks a player from the game as soon as it is safe to do so.
+ ///
+ /// The player to kick.
+ /// Whether to ban the player.
+ /// Custom reason message for the kick.
+ /// Whether this is an anti-cheat related kick.
+ /// Whether to bypass the data collection check.
+ /// Whether to force a ban regardless of settings.
+ internal static void Kick(this PlayerControl player, bool ban = false, string setReasonInfo = "", bool antiCheatBan = false, bool bypassDataCheck = false, bool forceBan = false)
+ {
+ if (!player.CanKick(ban, antiCheatBan, bypassDataCheck, forceBan, out var shouldBan)) return;
+ KickCooldownManager.ScheduleAction(() => { player.PerformKick(shouldBan, setReasonInfo, antiCheatBan); });
+ }
+
+ ///
+ /// Kicks a player from the game immediately if it is safe to do so, otherwise does nothing.
+ /// This should be used instead of in places where this method is repeatedly called, like Update methods for example.
+ ///
+ /// The player to kick.
+ /// Whether to ban the player.
+ /// Custom reason message for the kick.
+ /// Whether this is an anti-cheat related kick.
+ /// Whether to bypass the data collection check.
+ /// Whether to force a ban regardless of settings.
+ internal static void TryKick(this PlayerControl player, bool ban = false, string setReasonInfo = "", bool antiCheatBan = false, bool bypassDataCheck = false, bool forceBan = false)
+ {
+ if (!KickCooldownManager.IsReady()) return;
+ if (!player.CanKick(ban, antiCheatBan, bypassDataCheck, forceBan, out var shouldBan)) return;
+ KickCooldownManager.Trigger();
+ player.PerformKick(shouldBan, setReasonInfo, antiCheatBan);
+ }
+
+ ///
+ /// Kicks a player from the game immediately, regardless of cooldown.
+ /// Only use this if the target player is extremely dangerous and must be removed immediately.
+ ///
+ /// The player to kick.
+ /// Whether to ban the player.
+ /// Custom reason message for the kick.
+ /// Whether this is an anti-cheat related kick.
+ /// Whether to bypass the data collection check.
+ /// Whether to force a ban regardless of settings.
+ internal static void KickImmediate(this PlayerControl player, bool ban = false, string setReasonInfo = "", bool antiCheatBan = false, bool bypassDataCheck = false, bool forceBan = false)
+ {
+ if (!player.CanKick(ban, antiCheatBan, bypassDataCheck, forceBan, out var shouldBan)) return;
+ player.PerformKick(shouldBan, setReasonInfo, antiCheatBan);
+ }
+
+ ///
+ /// Determines whether the specified player can be kicked from the game and sets the outcome.
+ ///
+ /// The player to evaluate.
+ /// Whether to ban the player.
+ /// Whether this is an anti-cheat related kick.
+ /// Whether to bypass the data collection check.
+ /// Whether to force a ban regardless of settings.
+ ///
+ /// When the method returns, this value indicates whether the player should be banned
+ /// based on the provided flags and internal checks.
+ ///
+ /// true if the player can be kicked/banned, false otherwise.
+ private static bool CanKick(this PlayerControl player, bool ban, bool antiCheatBan, bool bypassDataCheck,
+ bool forceBan, out bool shouldBan)
+ {
+ shouldBan = ban || forceBan;
+
+ if (!GameState.IsHost || player.IsLocalPlayer() || (!player.DataIsCollected() && !bypassDataCheck) || player.IsHost() || player.isDummy)
+ return false;
+
+ if (forceBan || !antiCheatBan) return true;
+
+ return HandleAntiCheatBanCheck(shouldBan, out shouldBan);
+ }
+
+ ///
+ /// Determines whether a player should be banned when an anti‑cheat check is performed.
+ ///
+ /// Whether the player has already been flagged for banning before this method is called.
+ /// The output value that indicates whether the player should ultimately be banned.
+ ///
+ /// true if the player can be kicked or banned.
+ /// false if is set to 0 (Notify).
+ ///
+ private static bool HandleAntiCheatBanCheck(bool ban, out bool shouldBan)
+ {
+ shouldBan = ban && BetterGameSettings.WhenCheating.GetStringIndex() == 2;
+ return BetterGameSettings.WhenCheating.GetStringIndex() != 0;
+ }
+
///
/// Kicks a player from the game with optional ban and anti-cheat features.
///
/// The player to kick.
/// Whether to ban the player.
/// Custom reason message for the kick.
- /// Whether this is an anti-cheat related kick.
- /// Whether to bypass the data collection check.
- /// Whether to force a ban regardless of settings.
- internal static void Kick(this PlayerControl player, bool ban = false, string setReasonInfo = "", bool AntiCheatBan = false, bool bypassDataCheck = false, bool forceBan = false)
+ /// Whether this is an anti-cheat related kick.
+ private static void PerformKick(this PlayerControl player, bool ban = false, string setReasonInfo = "", bool antiCheatBan = false)
{
- var Ban = ban || forceBan;
-
- if (!GameState.IsHost || player.IsLocalPlayer() || (!player.DataIsCollected() && !bypassDataCheck) || player.IsHost() || player.isDummy)
- {
- return;
- }
-
- if (AntiCheatBan)
- {
- if (BetterGameSettings.WhenCheating.GetStringIndex() == 0 && !forceBan)
- {
- return;
- }
-
- Ban = (Ban && BetterGameSettings.WhenCheating.GetStringIndex() == 2) || forceBan;
- }
-
if (setReasonInfo != "")
{
- PlayerJoinAndLeftPatch.BetterShowNotification(player.Data, forceReasonText: string.Format(setReasonInfo, Ban ? TranslationStrings.AntiCheat_Ban.LocalizedString.ToLower() : TranslationStrings.AntiCheat_Kick.LocalizedString.ToLower()));
+ PlayerJoinAndLeftPatch.BetterShowNotification(player.Data, forceReasonText: string.Format(setReasonInfo, ban ? TranslationStrings.AntiCheat_Ban.LocalizedString.ToLower() : TranslationStrings.AntiCheat_Kick.LocalizedString.ToLower()));
}
- AmongUsClient.Instance.KickPlayer(player.GetClientId(), Ban);
+ AmongUsClient.Instance.KickPlayer(player.GetClientId(), ban);
- player.ExtendedData().AntiCheatInfo.BannedByAntiCheat = AntiCheatBan;
+ player.ExtendedData().AntiCheatInfo.BannedByAntiCheat = antiCheatBan;
}
///
diff --git a/src/build/Generated/TranslationStrings.cs b/src/build/Generated/TranslationStrings.cs
index 9878aeb..2cba8ab 100644
--- a/src/build/Generated/TranslationStrings.cs
+++ b/src/build/Generated/TranslationStrings.cs
@@ -388,6 +388,11 @@ public static class TranslationStrings
///
public static readonly TranslationString BetterSetting_TextHeader_Detections = new("BetterSetting.TextHeader.Detections");
+ ///
+ /// Base Translation: Kick cooldown
+ ///
+ public static readonly TranslationString BetterSetting_Setting_KickCooldown = new("BetterSetting.Setting.KickCooldown");
+
///
/// Base Translation: When a player is caught cheating
///