diff --git a/HNSRevamped.cs b/HNSRevamped.cs index b8e2d22..cdbe9d4 100644 --- a/HNSRevamped.cs +++ b/HNSRevamped.cs @@ -21,6 +21,8 @@ public partial class Main : BasePlugin HNSRevamped.Logger.Enable(); Instance = this; + BanManager.Init(); + Harmony.PatchAll(); } diff --git a/Modules/BanManager.cs b/Modules/BanManager.cs new file mode 100644 index 0000000..baf74e4 --- /dev/null +++ b/Modules/BanManager.cs @@ -0,0 +1,153 @@ +using HarmonyLib; +using InnerNet; +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +// https://github.com/EnhancedNetwork/TownofHost-Enhanced/blob/main/Modules/BanManager.cs +namespace HNSRevamped; + +public static class BanManager +{ + public static string RemoveHtmlTags(this string str) => Regex.Replace(str, "<[^>]*?>", ""); + private static string BanListPath = "./HNSR-DATA/BanList.txt"; + private static string ModeratorListPath = "./HNSR-DATA/ModeratorList.txt"; + public static List TempBanWhiteList = []; + public static void Init() + { + try + { + Directory.CreateDirectory("HNSR-DATA"); + + if (!File.Exists(BanListPath)) + { + Logger.Warn("Creating a new BanList.txt file", "BanManager"); + File.Create(BanListPath).Close(); + } + if (!File.Exists(ModeratorListPath)) + { + Logger.Warn("Creating a new ModeratorList.txt file", "BanManager"); + File.Create(ModeratorListPath).Close(); + } + + } + catch (Exception ex) + { + Logger.Exception(ex, "BanManager"); + } + } + private static string GetResourcesTxt(string path) + { + var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(path); + stream.Position = 0; + using StreamReader reader = new(stream, Encoding.UTF8); + return reader.ReadToEnd(); + } + public static string GetHashedPuid(this ClientData player) + { + if (player == null) return ""; + string puid = player.ProductUserId; + return GetHashedPuid(puid); + } + public static string GetHashedPuid(string puid) + { + using SHA256 sha256 = SHA256.Create(); + + // get sha-256 hash + byte[] sha256Bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(puid)); + string sha256Hash = BitConverter.ToString(sha256Bytes).Replace("-", "").ToLower(); + + // pick front 5 and last 4 + return string.Concat(sha256Hash.AsSpan(0, 5), sha256Hash.AsSpan(sha256Hash.Length - 4)); + } + + public static void AddBanPlayer(ClientData player) + { + if (!AmongUsClient.Instance.AmHost || player == null) return; + if (!CheckBanList(player?.FriendCode, player?.GetHashedPuid()) && !TempBanWhiteList.Contains(player?.GetHashedPuid())) + { + if (player?.GetHashedPuid() != "" && player?.GetHashedPuid() != null && player?.GetHashedPuid() != "e3b0cb855") + { + var additionalInfo = ""; + File.AppendAllText(BanListPath, $"{player?.FriendCode},{player?.GetHashedPuid()},{player.PlayerName.RemoveHtmlTags()}{additionalInfo}\n"); + } + else Logger.Info($"Failed to add player {player?.PlayerName.RemoveHtmlTags()}/{player?.FriendCode}/{player?.GetHashedPuid()} to the BanList", "AddBanPlayer"); + } + } + + public static void CheckBanPlayer(ClientData player) + { + if (!AmongUsClient.Instance.AmHost) return; + + string friendcode = player?.FriendCode; + + // Check file BanList.txt + if (Options.ApplyBanList.GetBool() && CheckBanList(friendcode, player?.GetHashedPuid())) + { + AmongUsClient.Instance.KickPlayer(player.Id, true); + Logger.Info($"{player.PlayerName} was in the BanList and has been banned", "BanListBan"); + return; + } + if (TempBanWhiteList.Contains(player?.GetHashedPuid())) + { + AmongUsClient.Instance.KickPlayer(player.Id, true); + //This should not happen + Logger.Info($"{player.PlayerName} was in the Temporary BanList", "TempBan"); + return; + } + } + public static bool CheckBanList(string code, string hashedpuid = "") + { + bool OnlyCheckPuid = false; + if (code == "" && hashedpuid != "") OnlyCheckPuid = true; + else if (code == "") return false; + + string noDiscrim = ""; + if (code.Contains('#')) + { + int index = code.IndexOf('#'); + noDiscrim = code[..index]; + } + + try + { + Directory.CreateDirectory("HNSR-DATA"); + if (!File.Exists(BanListPath)) File.Create(BanListPath).Close(); + using StreamReader sr = new(BanListPath); + string line; + while ((line = sr.ReadLine()) != null) + { + if (line == "") continue; + if (!OnlyCheckPuid) + { + if (line.Contains(code)) return true; + if (!string.IsNullOrEmpty(noDiscrim) && !line.Contains('#') && line.Contains(noDiscrim)) return true; + } + if (line.Contains(hashedpuid)) return true; + } + } + catch (Exception ex) + { + Logger.Exception(ex, "CheckBanList"); + } + return false; + } + +} +[HarmonyPatch(typeof(BanMenu), nameof(BanMenu.Select))] +class BanMenuSelectPatch +{ + public static void Postfix(BanMenu __instance, int clientId) + { + ClientData recentClient = AmongUsClient.Instance.GetRecentClient(clientId); + if (recentClient == null) return; + + if (!BanManager.CheckBanList(recentClient?.FriendCode, recentClient?.GetHashedPuid())) + __instance.BanButton.GetComponent().SetEnabledColors(); + } +} diff --git a/Modules/CustomOptionsHolder.cs b/Modules/CustomOptionsHolder.cs index 6aa768d..c3d49a6 100644 --- a/Modules/CustomOptionsHolder.cs +++ b/Modules/CustomOptionsHolder.cs @@ -23,9 +23,18 @@ namespace HNSRevamped taskOptionsLoad.Wait(); } - public static OptionItem Testy; - public static OptionItem Testy1; - public static OptionItem Testy2; + //System + public static OptionItem KickLowLevelPlayer; + public static OptionItem TempBanLowLevelPlayer; + + public static OptionItem KickInvalidFriendCodes; + public static OptionItem TempBanInvalidFriendCodes; + + public static OptionItem ApplyBanList; + public static OptionItem ApplyModeratorList; + + //Mod + public static OptionItem DisableTasks; public static bool IsLoaded = false; @@ -35,11 +44,20 @@ namespace HNSRevamped if (IsLoaded) return; //Main settings - Testy = IntegerOptionItem.Create(3, "Testy", new IntegerValueRule(0, 50, 5), 10, TabGroup.GameModifiers, false); - Testy1 = BooleanOptionItem.Create(4, "Testy1", true, TabGroup.ModSettings, false) - .SetColor(Color.red); - Testy2 = BooleanOptionItem.Create(5, "Testy2", true, TabGroup.ModSettings, false) - .SetParent(Testy1); + KickLowLevelPlayer = IntegerOptionItem.Create(60050, "KickLowLevelPlayer", new(0, 100, 1), 0, TabGroup.SystemSettings, false) + .SetValueFormat(OptionFormat.Level) + .SetHeader(true); + TempBanLowLevelPlayer = BooleanOptionItem.Create(60051, "TempBanLowLevelPlayer", false, TabGroup.SystemSettings, false) + .SetParent(KickLowLevelPlayer) + .SetValueFormat(OptionFormat.Times); + + KickInvalidFriendCodes = BooleanOptionItem.Create(60080, "KickInvalidFriendCodes", true, TabGroup.SystemSettings, false); + TempBanInvalidFriendCodes = BooleanOptionItem.Create(60081, "TempBanInvalidFriendCodes", false, TabGroup.SystemSettings, false) + .SetParent(KickInvalidFriendCodes); + + ApplyBanList = BooleanOptionItem.Create(60110, "ApplyBanList", true, TabGroup.SystemSettings, true); + ApplyModeratorList = BooleanOptionItem.Create(60120, "ApplyModeratorList", false, TabGroup.SystemSettings, false); + IsLoaded = true; } } diff --git a/Modules/OptionItem/OptionItem.cs b/Modules/OptionItem/OptionItem.cs index 3577c28..083d12a 100644 --- a/Modules/OptionItem/OptionItem.cs +++ b/Modules/OptionItem/OptionItem.cs @@ -213,8 +213,8 @@ namespace HNSRevamped public enum TabGroup { + SystemSettings, ModSettings, - GameModifiers, } @@ -225,5 +225,7 @@ namespace HNSRevamped Seconds, Percent, Multiplier, + Level, + Times, } } \ No newline at end of file diff --git a/Modules/Utils.cs b/Modules/Utils.cs index 6c06ce4..f9568c4 100644 --- a/Modules/Utils.cs +++ b/Modules/Utils.cs @@ -24,16 +24,23 @@ namespace HNSRevamped; public static string ColorString(Color32 color, string str) => $"<#{color.r:x2}{color.g:x2}{color.b:x2}{color.a:x2}>{str}"; public static string ColorToHex(Color32 color) => $"#{color.r:x2}{color.g:x2}{color.b:x2}{color.a:x2}"; + public static bool IsPlayerModerator(string friendCode) + { + if (friendCode == "") return false; + var friendCodesFilePath = @"./HNSR-DATA/Moderators.txt"; + var friendCodes = File.ReadAllLines(friendCodesFilePath); + return friendCodes.Any(code => code.Contains(friendCode)); + } public static string GetTabName(TabGroup tab) { switch (tab) { + case TabGroup.SystemSettings: + return "System Settings"; case TabGroup.ModSettings: return "Mod Settings"; - case TabGroup.GameModifiers: - return "Game Modifiers"; default: return ""; diff --git a/Patches/GameSettingMenuPatch.cs b/Patches/GameSettingMenuPatch.cs index 335395e..8247120 100644 --- a/Patches/GameSettingMenuPatch.cs +++ b/Patches/GameSettingMenuPatch.cs @@ -115,12 +115,12 @@ namespace HNSRevamped __instance.MenuDescriptionText.DestroyTranslator(); switch ((TabGroup)(tabNum - 3)) { + case TabGroup.SystemSettings: + __instance.MenuDescriptionText.text = "System Settings"; + break; case TabGroup.ModSettings: __instance.MenuDescriptionText.text = "Mod Settings"; break; - case TabGroup.GameModifiers: - __instance.MenuDescriptionText.text = "Game Modifiers"; - break; } } diff --git a/Patches/JoinAndLeavePatches.cs b/Patches/JoinAndLeavePatches.cs new file mode 100644 index 0000000..5e3e896 --- /dev/null +++ b/Patches/JoinAndLeavePatches.cs @@ -0,0 +1,61 @@ +using AmongUs.Data; +using HarmonyLib; +using AmongUs.GameOptions; +using InnerNet; +using System.Text.RegularExpressions; +using TMPro; +using UnityEngine; +using System.Linq; + + +namespace HNSRevamped; + +[HarmonyPatch(typeof(AmongUsClient), nameof(AmongUsClient.OnPlayerJoined))] +class OnPlayerJoinedPatch +{ + public static bool HasInvalidFriendCode(string friendcode) + { + if (string.IsNullOrEmpty(friendcode)) + { + return true; + } + + if (friendcode.Count(c => c == '#') != 1) + { + return true; + } + + string pattern = @"[\W\d]"; + if (Regex.IsMatch(friendcode[..friendcode.IndexOf("#")], pattern)) + { + return true; + } + + return false; + } + + static void Postfix([HarmonyArgument(0)] ClientData Client) + { + Logger.Info($" {Client.PlayerName} / {Client.FriendCode} / {Client.PlatformData.Platform}", "Joined The Game"); + + if (AmongUsClient.Instance.AmHost) + { + BanManager.CheckBanPlayer(Client); + + + if (HasInvalidFriendCode(Client.FriendCode) && Options.KickInvalidFriendCodes.GetBool()) + { + if (!Options.TempBanInvalidFriendCodes.GetBool()) + { + AmongUsClient.Instance.KickPlayer(Client.Id, false); + Logger.Info($" {Client.PlayerName} Was kicked for having an invalid FriendCode", "KickInvalidFriendCode"); + } + else + { + AmongUsClient.Instance.KickPlayer(Client.Id, true); + Logger.Info($" {Client.PlayerName} Was banned for having an invalid FriendCode", "BanInvalidFriendCode"); + } + } + } + } +} \ No newline at end of file diff --git a/Patches/KickPlayerPatch.cs b/Patches/KickPlayerPatch.cs new file mode 100644 index 0000000..d3df023 --- /dev/null +++ b/Patches/KickPlayerPatch.cs @@ -0,0 +1,29 @@ +using HarmonyLib; +using InnerNet; +using System.Collections.Generic; +using UnityEngine; + +// https://github.com/tukasa0001/TownOfHost/blob/main/Patches/ClientPatch.cs +namespace HNSRevamped; + +[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.KickPlayer))] +internal class KickPlayerPatch +{ + public static Dictionary AttemptedKickPlayerList = []; + public static bool Prefix(InnerNetClient __instance, int clientId, bool ban) + { + if (!AmongUsClient.Instance.AmHost) return true; + + var HashedPuid = AmongUsClient.Instance.GetClient(clientId).GetHashedPuid(); + if (!AttemptedKickPlayerList.ContainsKey(HashedPuid)) + AttemptedKickPlayerList.Add(HashedPuid, 0); + else if (AttemptedKickPlayerList[HashedPuid] < 10) + { + Logger.Fatal($"Kick player Request too fast! Canceled.", "KickPlayerPatch"); + return false; + } + if (ban) BanManager.AddBanPlayer(AmongUsClient.Instance.GetRecentClient(clientId)); + + return true; + } +} \ No newline at end of file diff --git a/Patches/LobbyViewSettingsPatch.cs b/Patches/LobbyViewSettingsPatch.cs index 455b023..36402f2 100644 --- a/Patches/LobbyViewSettingsPatch.cs +++ b/Patches/LobbyViewSettingsPatch.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using Object = UnityEngine.Object; +// https://github.com/Rabek009/MoreGamemodes/blob/master/Patches/LobbyViewSettingsPatch.cs namespace HNSRevamped { [HarmonyPatch(typeof(LobbyViewSettingsPane))] diff --git a/Patches/NumberOptionsPatch.cs b/Patches/NumberOptionsPatch.cs index fc882f9..89d0074 100644 --- a/Patches/NumberOptionsPatch.cs +++ b/Patches/NumberOptionsPatch.cs @@ -10,11 +10,15 @@ public static class NumberOption_Increase { public static bool Prefix(NumberOption __instance) { - __instance.Value += __instance.Increment; - __instance.UpdateValue(); - __instance.OnValueChanged.Invoke(__instance); - __instance.AdjustButtonsActiveState(); - return false; + if (Utils.isHideNSeek) + { + __instance.Value += __instance.Increment; + __instance.UpdateValue(); + __instance.OnValueChanged.Invoke(__instance); + __instance.AdjustButtonsActiveState(); + return false; + } + else return true; } } @@ -23,12 +27,15 @@ public static class NumberOption_Decrease { public static bool Prefix(NumberOption __instance) { - __instance.Value -= __instance.Increment; - __instance.UpdateValue(); - __instance.OnValueChanged.Invoke(__instance); - __instance.AdjustButtonsActiveState(); - return false; - + if (Utils.isHideNSeek) + { + __instance.Value -= __instance.Increment; + __instance.UpdateValue(); + __instance.OnValueChanged.Invoke(__instance); + __instance.AdjustButtonsActiveState(); + return false; + } + else return true; } } @@ -37,6 +44,9 @@ public static class NumberOption_Initialize { public static void Postfix(NumberOption __instance) { - __instance.ValidRange = new FloatRange(-999f, 999f); + if (Utils.isHideNSeek) + { + __instance.ValidRange = new FloatRange(-999f, 999f); + } } } \ No newline at end of file diff --git a/Patches/PlayerControlPatches.cs b/Patches/PlayerControlPatches.cs new file mode 100644 index 0000000..529397f --- /dev/null +++ b/Patches/PlayerControlPatches.cs @@ -0,0 +1,31 @@ +using AmongUs.Data; +using HarmonyLib; +using AmongUs.GameOptions; +using InnerNet; + +namespace HNSRevamped; +[HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.FixedUpdate))] +class FixedUpdateInGamePatch +{ + public static void Postfix(PlayerControl __instance) + { + if (__instance == null || __instance.PlayerId == 255) return; + + if (AmongUsClient.Instance.AmHost) + { + if (__instance.Data.PlayerLevel != 0 && __instance.Data.PlayerLevel < Options.KickLowLevelPlayer.GetInt() && __instance.Data.ClientId != AmongUsClient.Instance.HostId) + { + if (!Options.TempBanLowLevelPlayer.GetBool()) + { + AmongUsClient.Instance.KickPlayer(__instance.Data.ClientId, false); + Logger.Info($" {__instance.Data.PlayerName} was kicked for being under level {Options.KickLowLevelPlayer.GetInt()}", "KickLowLevelPlayer"); + } + else + { + AmongUsClient.Instance.KickPlayer(__instance.Data.ClientId, true); + Logger.Info($" {__instance.Data.PlayerName} was banned for being under level {Options.KickLowLevelPlayer.GetInt()} ", "BanLowLevelPlayer"); + } + } + } + } +} \ No newline at end of file