From 63a2bcbbe6f7c935a7179816cf8a79400889df26 Mon Sep 17 00:00:00 2001 From: At0mBomba <102352627+At0mBomba@users.noreply.github.com> Date: Wed, 22 Jul 2026 17:37:32 +0300 Subject: [PATCH 01/19] Move to regex for filter lists --- src/Data/BetterDataManager.cs | 260 ++++++++++++++---- .../AntiCheat/RPCHandlers/SendChatHandler.cs | 7 +- src/Modules/TextFileHandler.cs | 67 +---- .../Gameplay/Player/PlayerJoinAndLeftPatch.cs | 3 +- .../Gameplay/UI/Settings/GameSettingsPatch.cs | 10 +- src/Resources/Lang/en_US.json | 5 +- src/build/Generated/TranslationStrings.cs | 11 +- 7 files changed, 238 insertions(+), 125 deletions(-) diff --git a/src/Data/BetterDataManager.cs b/src/Data/BetterDataManager.cs index 78ad325..fad8c7e 100644 --- a/src/Data/BetterDataManager.cs +++ b/src/Data/BetterDataManager.cs @@ -1,4 +1,5 @@ -using BetterAmongUs.Data.Config; +using System.Text.RegularExpressions; +using BetterAmongUs.Data.Config; using BetterAmongUs.Data.Json; using BetterAmongUs.Utilities; using UnityEngine; @@ -102,9 +103,81 @@ internal static class BetterDataManager internal static readonly string banNameListFilePath = Path.Combine(Folders.saveInfoFolderPath, "BanNameList.txt"); /// - /// File containing banned words/patterns. + /// Legacy file containing banned words/patterns. /// - internal static readonly string banWordListFilePath = Path.Combine(Folders.saveInfoFolderPath, "BanWordList.txt"); + internal static readonly string banWordListFilePath_Legacy = Path.Combine(Folders.saveInfoFolderPath, "BanWordList.txt"); + + /// + /// File containing banned chat messages/patterns. + /// + internal static readonly string banChatListFilePath = Path.Combine(Folders.saveInfoFolderPath, "BanChatList.txt"); + } + + /// + /// Contains initial content for all files that require initialization. + /// + private static class Contents + { + /// + /// Example comment for BanPlayerList file. + /// + internal const string BAN_PLAYER_LIST_CONTENT = """ + // Example ban entries (friend code and/or hashed PUID) + // Format: [FriendCode], [HashedPUID] + // Example with both: + // FriendCode#0000, abc123def456789 + // Example with just friend code: + // FriendCode#0000 + // Example with just hashed PUID: + // , hash123xyz789 + """; + + /// + /// Example comment for BanNameList file. + /// + internal const string BAN_NAME_LIST_CONTENT = """ + // Example banned player name regex patterns + // Each pattern on a new line + // + // ^TNT$ + // ^hyde$ + // ^[\P{IsBasicLatin}\s]+$ + """; + + /// + /// Example comment for BanChatList file. + /// + internal const string BAN_CHAT_LIST_CONTENT = """ + // Example banned chat message regex patterns + // Each pattern on a new line + // + // (?i)(? + /// Legacy example comment for BanNameList file. + /// + internal const string BAN_NAME_LIST_CONTENT_LEGACY = """ + // Example banned player names + // Each name on a new line - supports wildcards with ** + // ** at start and end: contains anywhere + // ** at start only: ends with + // ** at end only: starts with + // No **: exact match (case-insensitive) + // + // HackerPlayer123 + // CheaterAccount + // **Bot** + // **Script + // Exploit** + // **Cheat** + """; } /// @@ -114,7 +187,7 @@ internal static class BetterDataManager [ Files.banPlayerListFilePath, Files.banNameListFilePath, - Files.banWordListFilePath + Files.banChatListFilePath ]; /// @@ -169,63 +242,26 @@ internal static class BetterDataManager foreach (var path in InitPaths) { - if (!File.Exists(path)) - { - var directory = Path.GetDirectoryName(path); - if (!Directory.Exists(directory)) - { - Directory.CreateDirectory(directory); - } + if (File.Exists(path)) continue; - using var writer = File.CreateText(path); - if (path == Files.banPlayerListFilePath) - { - writer.WriteLine("// Example ban entries (friend code and/or hashed PUID)"); - writer.WriteLine("// Format: [FriendCode], [HashedPUID]"); - writer.WriteLine("// Example with both:"); - writer.WriteLine("// FriendCode#0000, abc123def456789"); - writer.WriteLine("// Example with just friend code:"); - writer.WriteLine("// FriendCode#0000"); - writer.WriteLine("// Example with just hashed PUID:"); - writer.WriteLine("// , hash123xyz789"); - } - else if (path == Files.banNameListFilePath) - { - writer.WriteLine("// Example banned player names"); - writer.WriteLine("// Each name on a new line - supports wildcards with **"); - writer.WriteLine("// ** at start and end: contains anywhere"); - writer.WriteLine("// ** at start only: ends with"); - writer.WriteLine("// ** at end only: starts with"); - writer.WriteLine("// No **: exact match (case-insensitive)"); - writer.WriteLine("// "); - writer.WriteLine("// HackerPlayer123"); - writer.WriteLine("// CheaterAccount"); - writer.WriteLine("// **Bot**"); - writer.WriteLine("// **Script"); - writer.WriteLine("// Exploit**"); - writer.WriteLine("// **Cheat**"); - } - else if (path == Files.banWordListFilePath) - { - writer.WriteLine("// Example banned words/patterns"); - writer.WriteLine("// Each word or pattern on a new line - supports wildcards with **"); - writer.WriteLine("// ** at start and end: contains anywhere"); - writer.WriteLine("// ** at start only: ends with"); - writer.WriteLine("// ** at end only: starts with"); - writer.WriteLine("// No **: exact match (case-insensitive)"); - writer.WriteLine("// "); - writer.WriteLine("// hack"); - writer.WriteLine("// cheat"); - writer.WriteLine("// exploit"); - writer.WriteLine("// **bot**"); - writer.WriteLine("// **script**"); - writer.WriteLine("// modded"); - writer.WriteLine("// aimbot"); - writer.WriteLine("// wallhack"); - writer.WriteLine("// **hack**"); - writer.WriteLine("// **cheat**"); - writer.WriteLine("// speed**"); - } + var directory = Path.GetDirectoryName(path); + if (!Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + + using var writer = File.CreateText(path); + if (path == Files.banPlayerListFilePath) + { + writer.WriteLine(Contents.BAN_PLAYER_LIST_CONTENT); + } + else if (path == Files.banNameListFilePath) + { + writer.WriteLine(Contents.BAN_NAME_LIST_CONTENT); + } + else if (path == Files.banChatListFilePath) + { + writer.WriteLine(Contents.BAN_CHAT_LIST_CONTENT); } } } @@ -245,6 +281,110 @@ internal static class BetterDataManager { File.Move(Files.dataFilePath_Legacy, Files.dataFilePath); } + + HandleMigrationFromWildcardToRegex(); + } + + /// + /// Performs data migration from legacy wildcard‑based ban lists to the new regex‑based format. + /// + private static void HandleMigrationFromWildcardToRegex() + { + MigrateBanWordList(); + MigrateBanNameList(); + } + + /// + /// Migrates the legacy wildcard‑based BanWordList to the new regex-based BanChatList. + /// + private static void MigrateBanWordList() + { + if (!File.Exists(Files.banWordListFilePath_Legacy)) return; + + var content = File.ReadLines(Files.banWordListFilePath_Legacy) + .Where(line => + !string.IsNullOrWhiteSpace(line) && + !line.StartsWith("//") && + !line.StartsWith("#") + ).ToArray(); + + if (content.Any()) + { + using var writer = File.CreateText(Files.banChatListFilePath); + writer.WriteLine(Contents.BAN_CHAT_LIST_CONTENT); + writer.WriteLine(); + writer.WriteLine("// Your old wildcard based filters have been converted to regex patterns"); + writer.WriteLine("// They will behave exactly the same as before"); + writer.WriteLine("// You can keep using them without changing anything else"); + writer.WriteLine(); + foreach (var line in content) + { + writer.Write("(?i)(?: |^)"); + if (line.StartsWith("**")) writer.Write(".*"); + writer.Write(Regex.Escape(line.Trim('*'))); + if (line.EndsWith("**")) writer.Write(".*"); + writer.WriteLine("(?: |$)"); + } + } + File.Delete(Files.banWordListFilePath_Legacy); + } + + /// + /// Migrates the BanNameList from the legacy wildcard format to the current regex‑based format. + /// + private static void MigrateBanNameList() + { + if (!File.Exists(Files.banNameListFilePath)) return; + + var lines = File.ReadLines(Files.banNameListFilePath) + .Where(line => line != "// ") + .ToArray(); + + // Return if file contains new example comment + if (lines.AnyLineInContentLines(Contents.BAN_NAME_LIST_CONTENT)) + return; + + var content = lines.Where(line => + !string.IsNullOrWhiteSpace(line) && + !line.StartsWith("//") && + !line.StartsWith("#") + ).ToArray(); + + if (!content.Any()) + { + File.Delete(Files.banNameListFilePath); + return; + } + + if (!content.Any(s => Regex.IsMatch(s, @"\*\*")) && + !lines.AnyLineInContentLines(Contents.BAN_NAME_LIST_CONTENT_LEGACY)) return; + + using var writer = File.CreateText(Files.banNameListFilePath); + writer.WriteLine(Contents.BAN_NAME_LIST_CONTENT); + writer.WriteLine(); + writer.WriteLine("// Your old wildcard based filters have been converted to regex patterns"); + writer.WriteLine("// They will behave exactly the same as before"); + writer.WriteLine("// You can keep using them without changing anything else"); + writer.WriteLine(); + foreach (var line in content) + { + writer.Write("(?i)"); + if (!line.StartsWith("**")) writer.Write("^"); + writer.Write(Regex.Escape(line.Trim('*'))); + if (!line.EndsWith("**")) writer.Write("$"); + writer.WriteLine(); + } + } + + /// + /// Determines whether any line from the supplied array matches a line in the specified content string. + /// + /// An array of strings to search. + /// A multi‑line string whose individual lines are compared against . + /// true if at least one line from equals any line in , false otherwise. + private static bool AnyLineInContentLines(this string[] lines, string content) + { + return content.Split(Environment.NewLine).Any(lines.Contains); } /// diff --git a/src/Modules/AntiCheat/RPCHandlers/SendChatHandler.cs b/src/Modules/AntiCheat/RPCHandlers/SendChatHandler.cs index 720fd5b..2afad4e 100644 --- a/src/Modules/AntiCheat/RPCHandlers/SendChatHandler.cs +++ b/src/Modules/AntiCheat/RPCHandlers/SendChatHandler.cs @@ -17,11 +17,12 @@ internal sealed class SendChatHandler : RPCHandler { var text = reader.ReadString(); - if (BetterGameSettings.UseBanWordList.GetBool() && (!BetterGameSettings.UseBanWordListOnlyLobby.GetBool() || GameState.IsLobby)) + if (BetterGameSettings.UseBanChatList.GetBool() && (!BetterGameSettings.UseBanChatListOnlyLobby.GetBool() || GameState.IsLobby)) { - if (TextFileHandler.CompareStringFilters(BetterDataManager.Files.banWordListFilePath, text.Split(' '))) + if (TextFileHandler.CompareStringRegexMatches(BetterDataManager.Files.banChatListFilePath, text)) { - sender.Kick(false, $"has been kicked due to\nchat message containing a banned word!"); + var ban = BetterGameSettings.UseBanChatListBan.GetBool(); + sender.Kick(ban, $"has been {(ban ? "banned" : "kicked")} due to\nchat message matching a banned pattern!"); } } } diff --git a/src/Modules/TextFileHandler.cs b/src/Modules/TextFileHandler.cs index 658d100..6e94f63 100644 --- a/src/Modules/TextFileHandler.cs +++ b/src/Modules/TextFileHandler.cs @@ -9,25 +9,14 @@ namespace BetterAmongUs.Modules; internal static class TextFileHandler { /// - /// Compares strings against filter patterns in a file using wildcard matching. + /// Compares a string against regex patterns in a file. /// - /// Path to the filter file. - /// Array of strings to check against filters. - /// True if any string matches a filter pattern, false otherwise. - internal static bool CompareStringFilters(string filePath, string[] strings) + /// Path to the file containing regex patterns. + /// String to match against. + /// True if the string matches a regex pattern, false otherwise. + internal static bool CompareStringRegexMatches(string filePath, string inputString) { - foreach (var content in ReadContents(filePath)) - { - foreach (var text in strings) - { - if (CheckFilterString(content, text)) - { - return true; - } - } - } - - return false; + return ReadContents(filePath).Any(content => Regex.IsMatch(inputString, content, RegexOptions.Compiled)); } /// @@ -43,17 +32,10 @@ internal static class TextFileHandler StringComparer.OrdinalIgnoreCase ); - foreach (var content in ReadContents(filePath)) - { - string normalizedContent = content.ToLower().Trim(); - - if (stringSet.Contains(normalizedContent)) - { - return true; - } - } - - return false; + var contents = ReadContents(filePath) + .SelectMany(line => line.Split(',', StringSplitOptions.RemoveEmptyEntries)) + .Select(s => s.ToLower().Trim()); + return contents.Any(stringSet.Contains); } /// @@ -66,35 +48,16 @@ internal static class TextFileHandler if (File.Exists(filePath)) { return File.ReadLines(filePath) - .Where(line => !string.IsNullOrWhiteSpace(line) && - !line.StartsWith("//") && - !line.StartsWith("#")) - .SelectMany(line => line.Split(',', StringSplitOptions.RemoveEmptyEntries) - .Select(s => s.Trim())); + .Where(line => + !string.IsNullOrWhiteSpace(line) && + !line.StartsWith("//") && + !line.StartsWith("#") + ); } return []; } - /// - /// Checks if a text matches a filter pattern with wildcard support. - /// - /// The filter pattern (supports ** wildcards). - /// The text to check. - /// True if the text matches the filter pattern, false otherwise. - private static bool CheckFilterString(string filter, string text) - { - string pattern = filter switch - { - _ when filter.StartsWith("**") && filter.EndsWith("**") => Regex.Escape(filter.Trim('*')), // Contains anywhere - _ when filter.StartsWith("**") => Regex.Escape(filter.TrimStart('*')) + "$", // Ends with - _ when filter.EndsWith("**") => "^" + Regex.Escape(filter.TrimEnd('*')), // Starts with - _ => "^" + Regex.Escape(filter) + "$" // Exact match - }; - - return Regex.IsMatch(text, pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled); - } - /// /// Parses a YAML-like string into a dictionary of key-value pairs. /// diff --git a/src/Patches/Gameplay/Player/PlayerJoinAndLeftPatch.cs b/src/Patches/Gameplay/Player/PlayerJoinAndLeftPatch.cs index 99b5da5..4e8ba86 100644 --- a/src/Patches/Gameplay/Player/PlayerJoinAndLeftPatch.cs +++ b/src/Patches/Gameplay/Player/PlayerJoinAndLeftPatch.cs @@ -73,7 +73,8 @@ internal static class PlayerJoinAndLeftPatch { if (player != null) { - if (TextFileHandler.CompareStringFilters(BetterDataManager.Files.banNameListFilePath, [player.Data.PlayerName])) + if (TextFileHandler.CompareStringRegexMatches(BetterDataManager.Files.banNameListFilePath, + player.Data.PlayerName)) { player?.Kick(true, TranslationStrings.AntiCheat_BanPlayerListMessage.LocalizedString, bypassDataCheck: true); } diff --git a/src/Patches/Gameplay/UI/Settings/GameSettingsPatch.cs b/src/Patches/Gameplay/UI/Settings/GameSettingsPatch.cs index 05417a2..8c619fd 100644 --- a/src/Patches/Gameplay/UI/Settings/GameSettingsPatch.cs +++ b/src/Patches/Gameplay/UI/Settings/GameSettingsPatch.cs @@ -15,8 +15,9 @@ internal sealed class BetterGameSettings internal static OptionCheckboxItem? InvalidFriendCode; internal static OptionCheckboxItem? UseBanPlayerList; internal static OptionCheckboxItem? UseBanNameList; - internal static OptionCheckboxItem? UseBanWordList; - internal static OptionCheckboxItem? UseBanWordListOnlyLobby; + internal static OptionCheckboxItem? UseBanChatList; + internal static OptionCheckboxItem? UseBanChatListOnlyLobby; + internal static OptionCheckboxItem? UseBanChatListBan; internal static OptionCheckboxItem? DetectedLevel; internal static OptionIntItem? DetectedLevelAbove; internal static OptionCheckboxItem? KickLevel; @@ -73,8 +74,9 @@ internal static class GameSettingsPatch BetterGameSettings.CancelInvalidSabotage = OptionCheckboxItem.Create(BetterSettingsTab, TranslationStrings.BetterSetting_Setting_CancelInvalidSabotage, true); BetterGameSettings.UseBanPlayerList = OptionCheckboxItem.Create(BetterSettingsTab, TranslationStrings.BetterSetting_Setting_UseBanPlayerList, true); BetterGameSettings.UseBanNameList = OptionCheckboxItem.Create(BetterSettingsTab, TranslationStrings.BetterSetting_Setting_UseBanNameList, true); - BetterGameSettings.UseBanWordList = OptionCheckboxItem.Create(BetterSettingsTab, TranslationStrings.BetterSetting_Setting_UseBanWordList, true); - BetterGameSettings.UseBanWordListOnlyLobby = OptionCheckboxItem.Create(BetterSettingsTab, TranslationStrings.BetterSetting_Setting_UseBanWordListOnlyLobby, true, BetterGameSettings.UseBanWordList); + BetterGameSettings.UseBanChatList = OptionCheckboxItem.Create(BetterSettingsTab, TranslationStrings.BetterSetting_Setting_UseBanChatList, true); + BetterGameSettings.UseBanChatListOnlyLobby = OptionCheckboxItem.Create(BetterSettingsTab, TranslationStrings.BetterSetting_Setting_UseBanChatListOnlyLobby, true, BetterGameSettings.UseBanChatList); + BetterGameSettings.UseBanChatListBan = OptionCheckboxItem.Create(BetterSettingsTab, TranslationStrings.BetterSetting_Setting_UseBanChatListBan, false, BetterGameSettings.UseBanChatList); } // General detection settings diff --git a/src/Resources/Lang/en_US.json b/src/Resources/Lang/en_US.json index 3042bc2..b43d84b 100644 --- a/src/Resources/Lang/en_US.json +++ b/src/Resources/Lang/en_US.json @@ -87,8 +87,9 @@ "BetterSetting.Setting.CancelInvalidSabotage": "Cancel invalid sabotages", "BetterSetting.Setting.UseBanPlayerList": "Use ban player list", "BetterSetting.Setting.UseBanNameList": "Use ban name list", - "BetterSetting.Setting.UseBanWordList": "Use ban word list", - "BetterSetting.Setting.UseBanWordListOnlyLobby": "Only in lobby", + "BetterSetting.Setting.UseBanChatList": "Use ban chat list", + "BetterSetting.Setting.UseBanChatListOnlyLobby": "Only in lobby", + "BetterSetting.Setting.UseBanChatListBan": "Ban", "BetterSetting.Setting.ShowRoleForClients": "Show role in name for clients", "BetterSetting.Setting.CensorDetectionReason": "Censor detection reason", "BetterSetting.Setting.DetectedLevel": "Detected player levels", diff --git a/src/build/Generated/TranslationStrings.cs b/src/build/Generated/TranslationStrings.cs index 9878aeb..67b39d8 100644 --- a/src/build/Generated/TranslationStrings.cs +++ b/src/build/Generated/TranslationStrings.cs @@ -429,14 +429,19 @@ public static class TranslationStrings public static readonly TranslationString BetterSetting_Setting_UseBanNameList = new("BetterSetting.Setting.UseBanNameList"); /// - /// Base Translation: Use ban word list + /// Base Translation: Use ban chat list /// - public static readonly TranslationString BetterSetting_Setting_UseBanWordList = new("BetterSetting.Setting.UseBanWordList"); + public static readonly TranslationString BetterSetting_Setting_UseBanChatList = new("BetterSetting.Setting.UseBanChatList"); /// /// Base Translation: Only in lobby /// - public static readonly TranslationString BetterSetting_Setting_UseBanWordListOnlyLobby = new("BetterSetting.Setting.UseBanWordListOnlyLobby"); + public static readonly TranslationString BetterSetting_Setting_UseBanChatListOnlyLobby = new("BetterSetting.Setting.UseBanChatListOnlyLobby"); + + /// + /// Base Translation: Ban + /// + public static readonly TranslationString BetterSetting_Setting_UseBanChatListBan = new("BetterSetting.Setting.UseBanChatListBan"); /// /// Base Translation: Show role in name for clients From 175d6a8836a7f8f3d2cef97e1e0b95f479083307 Mon Sep 17 00:00:00 2001 From: At0mBomba <102352627+At0mBomba@users.noreply.github.com> Date: Wed, 19 Aug 2026 14:53:00 +0300 Subject: [PATCH 02/19] Add minimum player requirement for level kick --- src/Patches/Gameplay/Anticheat/CheckPlayerLevelPatch.cs | 8 ++++++-- src/Patches/Gameplay/UI/Settings/GameSettingsPatch.cs | 2 ++ src/Resources/Lang/en_US.json | 1 + src/build/Generated/TranslationStrings.cs | 5 +++++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Patches/Gameplay/Anticheat/CheckPlayerLevelPatch.cs b/src/Patches/Gameplay/Anticheat/CheckPlayerLevelPatch.cs index e18f9d2..e74778b 100644 --- a/src/Patches/Gameplay/Anticheat/CheckPlayerLevelPatch.cs +++ b/src/Patches/Gameplay/Anticheat/CheckPlayerLevelPatch.cs @@ -1,6 +1,6 @@ -using BetterAmongUs.Utilities; -using BetterAmongUs.Modules; +using BetterAmongUs.Modules; using BetterAmongUs.Patches.Gameplay.UI.Settings; +using BetterAmongUs.Utilities; using HarmonyLib; namespace BetterAmongUs.Patches.Gameplay.Anticheat; @@ -20,6 +20,10 @@ internal static class CheckPlayerLevelPatch // Kick players below minimum level if (!__instance.IsLocalPlayer() && BetterGameSettings.KickLevel.GetBool() && __instance.Data.PlayerLevel < BetterGameSettings.KickLevelBelow.GetInt()) { + var minPlayers = BetterGameSettings.KickLevelBelowMinimumPlayers.GetInt(); + if (minPlayers > 1 && minPlayers > BAUPlugin.AllPlayerControls.Count) + return; + __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/UI/Settings/GameSettingsPatch.cs b/src/Patches/Gameplay/UI/Settings/GameSettingsPatch.cs index ab230c1..0189fad 100644 --- a/src/Patches/Gameplay/UI/Settings/GameSettingsPatch.cs +++ b/src/Patches/Gameplay/UI/Settings/GameSettingsPatch.cs @@ -22,6 +22,7 @@ internal sealed class BetterGameSettings internal static OptionIntItem? DetectedLevelAbove; internal static OptionCheckboxItem? KickLevel; internal static OptionIntItem? KickLevelBelow; + internal static OptionIntItem? KickLevelBelowMinimumPlayers; internal static OptionCheckboxItem? DetectCheatClients; internal static OptionCheckboxItem? DetectInvalidRpcs; internal static OptionCheckboxItem? RpcRateLimiting; @@ -86,6 +87,7 @@ internal static class GameSettingsPatch BetterGameSettings.DetectedLevelAbove = OptionIntItem.Create(BetterSettingsTab, TranslationStrings.BetterSetting_Setting_DetectedLevelAbove, (100, 10000, 5), 500, ("Lv ", ""), BetterGameSettings.DetectedLevel); BetterGameSettings.KickLevel = OptionCheckboxItem.Create(BetterSettingsTab, TranslationStrings.BetterSetting_Setting_KickLevel, false); BetterGameSettings.KickLevelBelow = OptionIntItem.Create(BetterSettingsTab, TranslationStrings.BetterSetting_Setting_KickLevelBelow, (0, 10000, 1), 0, ("Lv ", ""), BetterGameSettings.KickLevel); + BetterGameSettings.KickLevelBelowMinimumPlayers = OptionIntItem.Create(BetterSettingsTab, TranslationStrings.BetterSetting_Setting_KickLevelBelowMinimumPlayers, (1, 15, 1), 9, parent: BetterGameSettings.KickLevelBelow); BetterGameSettings.DetectCheatClients = OptionCheckboxItem.Create(BetterSettingsTab, TranslationStrings.BetterSetting_Setting_DetectCheatClients, true); BetterGameSettings.DetectInvalidRpcs = OptionCheckboxItem.Create(BetterSettingsTab, TranslationStrings.BetterSetting_Setting_DetectInvalidRpcs, true); BetterGameSettings.RpcRateLimiting = OptionCheckboxItem.Create(BetterSettingsTab, TranslationStrings.BetterSetting_Setting_RpcRateLimiting, true); diff --git a/src/Resources/Lang/en_US.json b/src/Resources/Lang/en_US.json index ec8ec52..21c8886 100644 --- a/src/Resources/Lang/en_US.json +++ b/src/Resources/Lang/en_US.json @@ -96,6 +96,7 @@ "BetterSetting.Setting.DetectedLevelAbove": "Detected player level above", "BetterSetting.Setting.KickLevel": "Kick player levels", "BetterSetting.Setting.KickLevelBelow": "Kick player level below", + "BetterSetting.Setting.KickLevelBelowMinimumPlayers": "Only when player count at least", "BetterSetting.Setting.DetectCheatClients": "Detect cheat clients", "BetterSetting.Setting.DetectInvalidRpcs": "Detect invalid Rpcs", "BetterSetting.Setting.RpcRateLimiting": "Rpc Rate Limiting", diff --git a/src/build/Generated/TranslationStrings.cs b/src/build/Generated/TranslationStrings.cs index 2cba8ab..c02d623 100644 --- a/src/build/Generated/TranslationStrings.cs +++ b/src/build/Generated/TranslationStrings.cs @@ -473,6 +473,11 @@ public static class TranslationStrings /// public static readonly TranslationString BetterSetting_Setting_KickLevelBelow = new("BetterSetting.Setting.KickLevelBelow"); + /// + /// Base Translation: Only when player count at least + /// + public static readonly TranslationString BetterSetting_Setting_KickLevelBelowMinimumPlayers = new("BetterSetting.Setting.KickLevelBelowMinimumPlayers"); + /// /// Base Translation: Detect cheat clients /// From 4a49d392067a6b0b51af12507e37d0409b3214ce Mon Sep 17 00:00:00 2001 From: D1GQ Date: Fri, 21 Aug 2026 16:37:44 -0500 Subject: [PATCH 03/19] Clean up using partial classes --- src/Attributes/AutoRegisterAttribute.cs | 4 +- src/Attributes/RegisterInIl2Cpp.cs | 2 +- src/BAUPlugin.Constants.cs | 75 +++++++++++ src/BAUPlugin.ModInfo.cs | 121 +++++++++++++++++ src/BAUPlugin.cs | 4 +- src/Commands/DumpCommand.cs | 2 +- src/Data/BetterDataManager.cs | 99 +++----------- src/Managers/BAUUpdateManager.cs | 6 +- src/ModInfo.cs | 125 ------------------ src/Modules/Translator.cs | 2 +- src/MonoScripts/BetterPingTracker.cs | 2 +- .../Extended/ExtendedPlayerInfo.cs | 2 +- src/Network/Configs/BAUUpdateData.cs | 14 +- src/Network/GithubAPI.cs | 2 +- src/Network/Loaders/NewsLoader.cs | 2 +- src/Network/RPC.cs | 4 +- src/Patches/Client/ClientPatch.cs | 2 +- src/Patches/Client/Discord/DiscordPatch.cs | 2 +- .../Client/OptionsMenuBehaviourPatch.cs | 2 +- src/Patches/Gameplay/UI/Chat/ChatPatch.cs | 8 +- src/Patches/Unity/UnityWebRequestPatch.cs | 10 +- src/Utilities/Utils.cs | 2 +- 22 files changed, 248 insertions(+), 244 deletions(-) create mode 100644 src/BAUPlugin.Constants.cs create mode 100644 src/BAUPlugin.ModInfo.cs delete mode 100644 src/ModInfo.cs diff --git a/src/Attributes/AutoRegisterAttribute.cs b/src/Attributes/AutoRegisterAttribute.cs index 9482258..be03fe6 100644 --- a/src/Attributes/AutoRegisterAttribute.cs +++ b/src/Attributes/AutoRegisterAttribute.cs @@ -15,7 +15,7 @@ internal abstract class AutoRegisterAttribute : Attribute /// internal static void Initialize() { - var types = ModInfo.Assembly.GetTypes(); + var types = BAUPlugin.ModInfo.Assembly.GetTypes(); foreach (var type in types) { @@ -63,7 +63,7 @@ internal abstract class AutoRegisterAttribute : AutoRegisterAttribute where T /// protected override void Register() { - var types = ModInfo.Assembly.GetTypes(); + var types = BAUPlugin.ModInfo.Assembly.GetTypes(); foreach (var type in types) { diff --git a/src/Attributes/RegisterInIl2Cpp.cs b/src/Attributes/RegisterInIl2Cpp.cs index a4e43ce..3d854d8 100644 --- a/src/Attributes/RegisterInIl2Cpp.cs +++ b/src/Attributes/RegisterInIl2Cpp.cs @@ -21,7 +21,7 @@ internal class RegisterInIl2Cpp(params Type[] interfaces) : Attribute /// internal static void Initialize() { - var types = ModInfo.Assembly.GetTypes(); + var types = BAUPlugin.ModInfo.Assembly.GetTypes(); foreach (var type in types) { var attr = type.GetCustomAttribute(); diff --git a/src/BAUPlugin.Constants.cs b/src/BAUPlugin.Constants.cs new file mode 100644 index 0000000..3cd98a2 --- /dev/null +++ b/src/BAUPlugin.Constants.cs @@ -0,0 +1,75 @@ +namespace BetterAmongUs; + +internal partial class BAUPlugin +{ + /// + /// Contains constants That are used across BAU. + /// + internal static class Constants + { + /// + /// Example comment for BanPlayerList file. + /// + internal const string BAN_PLAYER_LIST_CONTENT = """ + // Example ban entries (friend code and/or hashed PUID) + // Format: [FriendCode], [HashedPUID] + // Example with both: + // FriendCode#0000, abc123def456789 + // Example with just friend code: + // FriendCode#0000 + // Example with just hashed PUID: + // , hash123xyz789 + """; + + /// + /// Example comment for BanNameList file. + /// + internal const string BAN_NAME_LIST_CONTENT = """ + // Example banned player name regex patterns + // Each pattern on a new line + // + // ^TNT$ + // ^hyde$ + // ^[\P{IsBasicLatin}\s]+$ + """; + + /// + /// Example comment for BanChatList file. + /// + internal const string BAN_CHAT_LIST_CONTENT = """ + // Example banned chat message regex patterns + // Each pattern on a new line + // + // (?i)(? + /// Legacy example comment for BanNameList file. + /// + internal const string BAN_NAME_LIST_CONTENT_LEGACY = """ + // Example banned player names + // Each name on a new line - supports wildcards with ** + // ** at start and end: contains anywhere + // ** at start only: ends with + // ** at end only: starts with + // No **: exact match (case-insensitive) + // + // HackerPlayer123 + // CheaterAccount + // **Bot** + // **Script + // Exploit** + // **Cheat** + """; + + internal const int MAX_CHAT_TEXT = 120; + internal const string BAU_CUSTOM_RPC_FLAG = "bau:rpc"; + internal const string BAU_MODDED_PROTOCOL_FLAG = "bau:flags"; + } +} \ No newline at end of file diff --git a/src/BAUPlugin.ModInfo.cs b/src/BAUPlugin.ModInfo.cs new file mode 100644 index 0000000..f8dee4b --- /dev/null +++ b/src/BAUPlugin.ModInfo.cs @@ -0,0 +1,121 @@ +using BetterAmongUs.Enums; +using System.Reflection; + +namespace BetterAmongUs; + +internal partial class BAUPlugin +{ + /// + /// Contains metadata and constants for the BetterAmongUs mod. + /// + internal static class ModInfo + { + /// + /// Gets the release type of the current build. + /// + internal static readonly ReleaseTypes ReleaseBuildType = ReleaseTypes.Release; + + /// + /// Gets the Git commit hash from assembly metadata. + /// + public static string CommitHash = ThisAssembly.Git.Commit; + + /// + /// Gets the build date from assembly metadata. + /// + public static string BuildDate = ThisAssembly.Metadata.BuildDate; + + /// + /// The beta number for beta releases. + /// + internal const string BETA_NUM = "0"; + + /// + /// The hotfix number for hotfix releases. + /// + internal const string HOTFIX_NUM = "1"; + + /// + /// Indicates whether this is a hotfix release. + /// + internal const bool IS_HOTFIX = true; + + /// + /// The name of BAU. + /// + internal const string PLUGIN_NAME = "BetterAmongUs"; + + /// + /// The GUID (Globally Unique Identifier) of BAU. + /// + internal const string PLUGIN_GUID = "com.d1gq.betteramongus"; + + /// + /// The version of BAU. + /// + internal const string PLUGIN_VERSION = "1.3.3"; + + /// + /// Gets the list of supported Among Us versions. + /// + internal static string[] SupportedAmongUsVersions = + [ + "2026.8.18" + ]; + + /// + /// The GitHub repository URL for BAU. + /// + internal const string GITHUB = ThisAssembly.Git.RepositoryUrl; + + /// + /// The Discord invite URL for BAU. + /// + internal const string DISCORD = "https://discord.gg/vjYrXpzNAn"; + + /// + /// Indicator rather that BAU is running on Starlight for Android. + /// + internal static readonly bool Starlight = OperatingSystem.IsAndroid(); + + /// + /// Retrieves metadata from the assembly attributes. + /// + /// The metadata key to retrieve. + /// The metadata value, or an empty string if not found. + private static string GetAssemblyMetadata(string key) + { + var attribute = Assembly + .GetCustomAttributes() + .FirstOrDefault(a => a.Key == key); + + return attribute?.Value ?? string.Empty; + } + + /// + /// The assembly associated to this mod. + /// + internal static Assembly Assembly + { + get + { + if (field == null) + { + field = Assembly.GetExecutingAssembly(); + } + return field; + } + } + + /// + /// Contains constants for Among Us. + /// + internal static class AmongUs + { + /// + /// The process name of the Among Us executable. + /// + internal const string PROCESS_NAME = "Among Us.exe"; + } + } +} \ No newline at end of file diff --git a/src/BAUPlugin.cs b/src/BAUPlugin.cs index 16476a6..f2a3405 100644 --- a/src/BAUPlugin.cs +++ b/src/BAUPlugin.cs @@ -24,7 +24,7 @@ namespace BetterAmongUs; [BepInPlugin(ModInfo.PLUGIN_GUID, ModInfo.PLUGIN_NAME, ModInfo.PLUGIN_VERSION)] [BepInProcess(ModInfo.AmongUs.PROCESS_NAME)] -internal class BAUPlugin : BasePlugin +internal partial class BAUPlugin : BasePlugin { /// /// Gets the formatted version text for display. @@ -86,7 +86,7 @@ internal class BAUPlugin : BasePlugin /// /// Gets platform-specific data. /// - internal static PlatformSpecificData PlatformData => Constants.GetPlatformData(); + internal static PlatformSpecificData PlatformData => global::Constants.GetPlatformData(); /// /// Gets the list of all PlayerControl instances. diff --git a/src/Commands/DumpCommand.cs b/src/Commands/DumpCommand.cs index c069a52..f6f9a01 100644 --- a/src/Commands/DumpCommand.cs +++ b/src/Commands/DumpCommand.cs @@ -49,7 +49,7 @@ internal sealed class DumpCommand : BaseCommand return; } - if (!ModInfo.Starlight) + if (!BAUPlugin.ModInfo.Starlight) { string log = File.ReadAllText(logFilePath); string newLog = DecryptLog(log); diff --git a/src/Data/BetterDataManager.cs b/src/Data/BetterDataManager.cs index fad8c7e..cec589c 100644 --- a/src/Data/BetterDataManager.cs +++ b/src/Data/BetterDataManager.cs @@ -1,7 +1,7 @@ -using System.Text.RegularExpressions; -using BetterAmongUs.Data.Config; +using BetterAmongUs.Data.Config; using BetterAmongUs.Data.Json; using BetterAmongUs.Utilities; +using System.Text.RegularExpressions; using UnityEngine; namespace BetterAmongUs.Data; @@ -106,80 +106,13 @@ internal static class BetterDataManager /// Legacy file containing banned words/patterns. /// internal static readonly string banWordListFilePath_Legacy = Path.Combine(Folders.saveInfoFolderPath, "BanWordList.txt"); - + /// /// File containing banned chat messages/patterns. /// internal static readonly string banChatListFilePath = Path.Combine(Folders.saveInfoFolderPath, "BanChatList.txt"); } - /// - /// Contains initial content for all files that require initialization. - /// - private static class Contents - { - /// - /// Example comment for BanPlayerList file. - /// - internal const string BAN_PLAYER_LIST_CONTENT = """ - // Example ban entries (friend code and/or hashed PUID) - // Format: [FriendCode], [HashedPUID] - // Example with both: - // FriendCode#0000, abc123def456789 - // Example with just friend code: - // FriendCode#0000 - // Example with just hashed PUID: - // , hash123xyz789 - """; - - /// - /// Example comment for BanNameList file. - /// - internal const string BAN_NAME_LIST_CONTENT = """ - // Example banned player name regex patterns - // Each pattern on a new line - // - // ^TNT$ - // ^hyde$ - // ^[\P{IsBasicLatin}\s]+$ - """; - - /// - /// Example comment for BanChatList file. - /// - internal const string BAN_CHAT_LIST_CONTENT = """ - // Example banned chat message regex patterns - // Each pattern on a new line - // - // (?i)(? - /// Legacy example comment for BanNameList file. - /// - internal const string BAN_NAME_LIST_CONTENT_LEGACY = """ - // Example banned player names - // Each name on a new line - supports wildcards with ** - // ** at start and end: contains anywhere - // ** at start only: ends with - // ** at end only: starts with - // No **: exact match (case-insensitive) - // - // HackerPlayer123 - // CheaterAccount - // **Bot** - // **Script - // Exploit** - // **Cheat** - """; - } - /// /// Array of file paths that should be checked during initialization. /// @@ -196,7 +129,7 @@ internal static class BetterDataManager /// The game installation path string. internal static string GetPathToAmongUs() { - if (!ModInfo.Starlight) + if (!BAUPlugin.ModInfo.Starlight) { return Path.GetDirectoryName(Application.dataPath) ?? throw new Exception("Unable to find `Application.dataPath` path!"); } @@ -221,7 +154,7 @@ internal static class BetterDataManager /// The persistent data path string. internal static string GetPathToAmongUsData() { - if (!ModInfo.Starlight) + if (!BAUPlugin.ModInfo.Starlight) { return Application.persistentDataPath; } @@ -253,15 +186,15 @@ internal static class BetterDataManager using var writer = File.CreateText(path); if (path == Files.banPlayerListFilePath) { - writer.WriteLine(Contents.BAN_PLAYER_LIST_CONTENT); + writer.WriteLine(BAUPlugin.Constants.BAN_PLAYER_LIST_CONTENT); } else if (path == Files.banNameListFilePath) { - writer.WriteLine(Contents.BAN_NAME_LIST_CONTENT); + writer.WriteLine(BAUPlugin.Constants.BAN_NAME_LIST_CONTENT); } else if (path == Files.banChatListFilePath) { - writer.WriteLine(Contents.BAN_CHAT_LIST_CONTENT); + writer.WriteLine(BAUPlugin.Constants.BAN_CHAT_LIST_CONTENT); } } } @@ -281,7 +214,7 @@ internal static class BetterDataManager { File.Move(Files.dataFilePath_Legacy, Files.dataFilePath); } - + HandleMigrationFromWildcardToRegex(); } @@ -307,11 +240,11 @@ internal static class BetterDataManager !line.StartsWith("//") && !line.StartsWith("#") ).ToArray(); - + if (content.Any()) { using var writer = File.CreateText(Files.banChatListFilePath); - writer.WriteLine(Contents.BAN_CHAT_LIST_CONTENT); + writer.WriteLine(BAUPlugin.Constants.BAN_CHAT_LIST_CONTENT); writer.WriteLine(); writer.WriteLine("// Your old wildcard based filters have been converted to regex patterns"); writer.WriteLine("// They will behave exactly the same as before"); @@ -335,13 +268,13 @@ internal static class BetterDataManager private static void MigrateBanNameList() { if (!File.Exists(Files.banNameListFilePath)) return; - + var lines = File.ReadLines(Files.banNameListFilePath) .Where(line => line != "// ") .ToArray(); - + // Return if file contains new example comment - if (lines.AnyLineInContentLines(Contents.BAN_NAME_LIST_CONTENT)) + if (lines.AnyLineInContentLines(BAUPlugin.Constants.BAN_NAME_LIST_CONTENT)) return; var content = lines.Where(line => @@ -357,10 +290,10 @@ internal static class BetterDataManager } if (!content.Any(s => Regex.IsMatch(s, @"\*\*")) && - !lines.AnyLineInContentLines(Contents.BAN_NAME_LIST_CONTENT_LEGACY)) return; + !lines.AnyLineInContentLines(BAUPlugin.Constants.BAN_NAME_LIST_CONTENT_LEGACY)) return; using var writer = File.CreateText(Files.banNameListFilePath); - writer.WriteLine(Contents.BAN_NAME_LIST_CONTENT); + writer.WriteLine(BAUPlugin.Constants.BAN_NAME_LIST_CONTENT); writer.WriteLine(); writer.WriteLine("// Your old wildcard based filters have been converted to regex patterns"); writer.WriteLine("// They will behave exactly the same as before"); diff --git a/src/Managers/BAUUpdateManager.cs b/src/Managers/BAUUpdateManager.cs index 9c78e09..9555572 100644 --- a/src/Managers/BAUUpdateManager.cs +++ b/src/Managers/BAUUpdateManager.cs @@ -1,13 +1,13 @@ using BepInEx.Unity.IL2CPP.Utils; using BetterAmongUs.Attributes; -using BetterAmongUs.Utilities; using BetterAmongUs.Modules.Support; using BetterAmongUs.Network.Loaders; +using BetterAmongUs.Utilities; +using BetterAmongUs.Utilities.Extension; using Il2CppInterop.Runtime.Attributes; using System.Collections; using TMPro; using UnityEngine; -using BetterAmongUs.Utilities.Extension; namespace BetterAmongUs.Managers; @@ -93,7 +93,7 @@ internal sealed class BAUUpdateManager : MonoBehaviour private void Start() { - var oldDll = ModInfo.Assembly.Location + ".old"; + var oldDll = BAUPlugin.ModInfo.Assembly.Location + ".old"; if (File.Exists(oldDll)) { File.Delete(oldDll); diff --git a/src/ModInfo.cs b/src/ModInfo.cs deleted file mode 100644 index 9bc7fd1..0000000 --- a/src/ModInfo.cs +++ /dev/null @@ -1,125 +0,0 @@ -using BetterAmongUs.Enums; -using System.Reflection; - -namespace BetterAmongUs; - -/// -/// Contains metadata and constants for the BetterAmongUs mod. -/// -internal static class ModInfo -{ - /// - /// Gets the release type of the current build. - /// - internal static readonly ReleaseTypes ReleaseBuildType = ReleaseTypes.Release; - - /// - /// Gets the Git commit hash from assembly metadata. - /// - public static string CommitHash = ThisAssembly.Git.Commit; - - /// - /// Gets the build date from assembly metadata. - /// - public static string BuildDate = ThisAssembly.Metadata.BuildDate; - - /// - /// The beta number for beta releases. - /// - internal const string BETA_NUM = "0"; - - /// - /// The hotfix number for hotfix releases. - /// - internal const string HOTFIX_NUM = "1"; - - /// - /// Indicates whether this is a hotfix release. - /// - internal const bool IS_HOTFIX = true; - - /// - /// The name of BAU. - /// - internal const string PLUGIN_NAME = "BetterAmongUs"; - - /// - /// The GUID (Globally Unique Identifier) of BAU. - /// - internal const string PLUGIN_GUID = "com.d1gq.betteramongus"; - - /// - /// The version of BAU. - /// - internal const string PLUGIN_VERSION = "1.3.3"; - - /// - /// Gets the list of supported Among Us versions. - /// - internal static string[] SupportedAmongUsVersions = - [ - "2026.8.18" - ]; - - /// - /// The GitHub repository URL for BAU. - /// - internal const string GITHUB = ThisAssembly.Git.RepositoryUrl; - - /// - /// The Discord invite URL for BAU. - /// - internal const string DISCORD = "https://discord.gg/vjYrXpzNAn"; - - /// - /// Indicator rather that BAU is running on Starlight for Android. - /// - internal static readonly bool Starlight = OperatingSystem.IsAndroid(); - - /// - /// Retrieves metadata from the assembly attributes. - /// - /// The metadata key to retrieve. - /// The metadata value, or an empty string if not found. - private static string GetAssemblyMetadata(string key) - { - var attribute = Assembly - .GetCustomAttributes() - .FirstOrDefault(a => a.Key == key); - - return attribute?.Value ?? string.Empty; - } - - /// - /// The assembly associated to this mod. - /// - internal static Assembly Assembly - { - get - { - if (field == null) - { - field = Assembly.GetExecutingAssembly(); - } - return field; - } - } - - /// - /// Contains constants for Among Us. - /// - internal static class AmongUs - { - /// - /// The process name of the Among Us executable. - /// - internal const string PROCESS_NAME = "Among Us.exe"; - } - - internal static class Constants - { - internal const int MAX_CHAT_TEXT = 120; - internal const string BAU_CUSTOM_RPC_FLAG = "bau:rpc"; - internal const string BAU_MODDED_PROTOCOL_FLAG = "bau:flags"; - } -} \ No newline at end of file diff --git a/src/Modules/Translator.cs b/src/Modules/Translator.cs index b429d44..063706a 100644 --- a/src/Modules/Translator.cs +++ b/src/Modules/Translator.cs @@ -34,7 +34,7 @@ internal static class Translator { try { - var assembly = ModInfo.Assembly; + var assembly = BAUPlugin.ModInfo.Assembly; var jsonFileNames = GetJsonResourceNames(assembly); TranslateMaps = []; diff --git a/src/MonoScripts/BetterPingTracker.cs b/src/MonoScripts/BetterPingTracker.cs index ceba402..9137d68 100644 --- a/src/MonoScripts/BetterPingTracker.cs +++ b/src/MonoScripts/BetterPingTracker.cs @@ -85,7 +85,7 @@ internal sealed class BetterPingTracker : MonoBehaviour } sb.Append($"BetterAmongUs {BAUPlugin.GetVersionText(true)}\n"); - sb.Append($"{ModInfo.GITHUB}\n".Size(52f)); + sb.Append($"{BAUPlugin.ModInfo.GITHUB}\n".Size(52f)); if (BAUConfigs.ShowFPS.Value) { diff --git a/src/MonoScripts/Extended/ExtendedPlayerInfo.cs b/src/MonoScripts/Extended/ExtendedPlayerInfo.cs index 1784cd5..a9a55f3 100644 --- a/src/MonoScripts/Extended/ExtendedPlayerInfo.cs +++ b/src/MonoScripts/Extended/ExtendedPlayerInfo.cs @@ -93,7 +93,7 @@ internal sealed class ExtendedPlayerInfo : MonoBehaviour, IMonoExtension modVersion) @@ -75,7 +75,7 @@ internal sealed class BAUUpdateData // 2. Versions are equal, compare release types var updateReleaseType = (ReleaseTypes)ReleaseType; - var currentReleaseType = ModInfo.ReleaseBuildType; + var currentReleaseType = BAUPlugin.ModInfo.ReleaseBuildType; // Release is always preferred over Beta if (updateReleaseType == ReleaseTypes.Release && currentReleaseType == ReleaseTypes.Beta) @@ -88,15 +88,15 @@ internal sealed class BAUUpdateData // 3. Same version and release type, compare specific numbers if (updateReleaseType == ReleaseTypes.Beta) { - return BetaNumber > int.Parse(ModInfo.BETA_NUM); + return BetaNumber > int.Parse(BAUPlugin.ModInfo.BETA_NUM); } // Release version - check hotfixes - if (IsHotfix && !ModInfo.IS_HOTFIX) + if (IsHotfix && !BAUPlugin.ModInfo.IS_HOTFIX) return true; - if (IsHotfix && ModInfo.IS_HOTFIX) - return HotfixNumber > int.Parse(ModInfo.HOTFIX_NUM); + if (IsHotfix && BAUPlugin.ModInfo.IS_HOTFIX) + return HotfixNumber > int.Parse(BAUPlugin.ModInfo.HOTFIX_NUM); // Same version, same type, no newer hotfix return false; @@ -132,7 +132,7 @@ internal sealed class BAUUpdateData } object waiting = true; - var dllPath = ModInfo.Assembly.Location; + var dllPath = BAUPlugin.ModInfo.Assembly.Location; yield return GitHubFile.CoDownloadFile(DllLink, dllPath + ".temp", path => { File.Move(dllPath, dllPath + ".old"); diff --git a/src/Network/GithubAPI.cs b/src/Network/GithubAPI.cs index a4b88de..11cdab4 100644 --- a/src/Network/GithubAPI.cs +++ b/src/Network/GithubAPI.cs @@ -80,7 +80,7 @@ internal sealed class GithubAPI : MonoBehaviour yield return newsLoader.CoFetchNewsData(); // Disable auto updating on Starlight - if (!ModInfo.Starlight) + if (!BAUPlugin.ModInfo.Starlight) { var updateLoader = gameObject.AddComponent(); yield return updateLoader.CoFetchUpdateData(); diff --git a/src/Network/Loaders/NewsLoader.cs b/src/Network/Loaders/NewsLoader.cs index 1fa3722..440946a 100644 --- a/src/Network/Loaders/NewsLoader.cs +++ b/src/Network/Loaders/NewsLoader.cs @@ -110,7 +110,7 @@ internal sealed class NewsLoader : MonoBehaviour private IEnumerator CoLoadNewsTest() { string yamlDirectory = "BetterAmongUs.Resources.NewsTest"; - var assembly = ModInfo.Assembly; + var assembly = BAUPlugin.ModInfo.Assembly; using Stream? resourceStream = assembly.GetManifestResourceStream(yamlDirectory); if (resourceStream != null) { diff --git a/src/Network/RPC.cs b/src/Network/RPC.cs index 5860518..5c0e85f 100644 --- a/src/Network/RPC.cs +++ b/src/Network/RPC.cs @@ -28,7 +28,7 @@ internal static class RPC { AmongUsClient.Instance.SendRpcImmediately(PlayerControl.LocalPlayer.MyPhysics.NetId, CUSTOM_RPC_CALL, SendOption.Reliable, writer => { - writer.Write(ModInfo.Constants.BAU_CUSTOM_RPC_FLAG); // Flag to check if its a rpc packed into SetNamePlateStr + writer.Write(BAUPlugin.Constants.BAU_CUSTOM_RPC_FLAG); // Flag to check if its a rpc packed into SetNamePlateStr writer.Write((byte)customRPC); action(writer); }, targetClientId); @@ -120,7 +120,7 @@ internal static class RPC { try { - if (reader.ReadString() == ModInfo.Constants.BAU_CUSTOM_RPC_FLAG) + if (reader.ReadString() == BAUPlugin.Constants.BAU_CUSTOM_RPC_FLAG) { return true; } diff --git a/src/Patches/Client/ClientPatch.cs b/src/Patches/Client/ClientPatch.cs index 4f6468b..da3d86a 100644 --- a/src/Patches/Client/ClientPatch.cs +++ b/src/Patches/Client/ClientPatch.cs @@ -39,7 +39,7 @@ internal static class ClientPatch private static bool SignInStatusComponent_SetOnline_Prefix() { // Get supported Among Us versions for BAU - var varSupportedVersions = ModInfo.SupportedAmongUsVersions; + var varSupportedVersions = BAUPlugin.ModInfo.SupportedAmongUsVersions; Version currentVersion = new(BAUPlugin.AppVersion); Version firstSupportedVersion = new(varSupportedVersions.First()); Version lastSupportedVersion = new(varSupportedVersions.Last()); diff --git a/src/Patches/Client/Discord/DiscordPatch.cs b/src/Patches/Client/Discord/DiscordPatch.cs index 02256c3..895a98d 100644 --- a/src/Patches/Client/Discord/DiscordPatch.cs +++ b/src/Patches/Client/Discord/DiscordPatch.cs @@ -50,7 +50,7 @@ internal static class DiscordPatch { // Streamer mode hides lobby code, only show mode info if (GameState.IsHideNSeek) - details = $"BAU v{ModInfo.PLUGIN_VERSION} - Hide & Seek"; + details = $"BAU v{BAUPlugin.ModInfo.PLUGIN_VERSION} - Hide & Seek"; } } catch diff --git a/src/Patches/Client/OptionsMenuBehaviourPatch.cs b/src/Patches/Client/OptionsMenuBehaviourPatch.cs index 26d1727..c30e7b4 100644 --- a/src/Patches/Client/OptionsMenuBehaviourPatch.cs +++ b/src/Patches/Client/OptionsMenuBehaviourPatch.cs @@ -60,7 +60,7 @@ internal static class OptionsMenuBehaviourPatch ClientOptionItem.CreateToggle(TranslationStrings.BetterOption_CompressSettingFiles, BAUConfigs.CompressSettingFiles, 2, __instance, ConvertAllSettingFiles); // Button options (no toggle) - if (!ModInfo.Starlight) + if (!BAUPlugin.ModInfo.Starlight) { ClientOptionItem.CreateButton(TranslationStrings.BetterOption_SaveData, -1, __instance, OpenSaveData, () => { diff --git a/src/Patches/Gameplay/UI/Chat/ChatPatch.cs b/src/Patches/Gameplay/UI/Chat/ChatPatch.cs index 32ea480..7c970f1 100644 --- a/src/Patches/Gameplay/UI/Chat/ChatPatch.cs +++ b/src/Patches/Gameplay/UI/Chat/ChatPatch.cs @@ -369,8 +369,8 @@ internal static class ChatPatch __instance.textArea.AllowSymbols = true; __instance.textArea.AllowPaste = true; __instance.textArea.AllowEmail = true; - __instance.textArea.characterLimit = ModInfo.Constants.MAX_CHAT_TEXT; - __instance.charCountText.text = "0/" + ModInfo.Constants.MAX_CHAT_TEXT; + __instance.textArea.characterLimit = BAUPlugin.Constants.MAX_CHAT_TEXT; + __instance.charCountText.text = "0/" + BAUPlugin.Constants.MAX_CHAT_TEXT; } [HarmonyPatch(typeof(FreeChatInputField), nameof(FreeChatInputField.UpdateCharCount))] @@ -379,7 +379,7 @@ internal static class ChatPatch { // Update character counter with color coding int length = __instance.textArea.text.Length; - __instance.charCountText.text = string.Format("{0}/" + ModInfo.Constants.MAX_CHAT_TEXT, length); + __instance.charCountText.text = string.Format("{0}/" + BAUPlugin.Constants.MAX_CHAT_TEXT, length); __instance.charCountText.color = GetCharColor(length); } @@ -387,7 +387,7 @@ internal static class ChatPatch { // Color gradient: green -> yellow -> red as text length increases Color[] colorGradient = [Color.green, Color.yellow, Color.red]; - (float min, float max) lerpRange = (0f, ModInfo.Constants.MAX_CHAT_TEXT - 1); + (float min, float max) lerpRange = (0f, BAUPlugin.Constants.MAX_CHAT_TEXT - 1); return colorGradient.LerpColor(lerpRange, length); } } diff --git a/src/Patches/Unity/UnityWebRequestPatch.cs b/src/Patches/Unity/UnityWebRequestPatch.cs index d45fc2d..cef2c01 100644 --- a/src/Patches/Unity/UnityWebRequestPatch.cs +++ b/src/Patches/Unity/UnityWebRequestPatch.cs @@ -16,15 +16,15 @@ internal static class UnityWebRequestPatch var stringBuilder = new StringBuilder(); // Format: "Version;BuildType;IsHotfix/HotfixNum/BetaNum" - stringBuilder.Append(ModInfo.PLUGIN_VERSION); + stringBuilder.Append(BAUPlugin.ModInfo.PLUGIN_VERSION); stringBuilder.Append(';'); - stringBuilder.Append(Enum.GetName(ModInfo.ReleaseBuildType)); + stringBuilder.Append(Enum.GetName(BAUPlugin.ModInfo.ReleaseBuildType)); stringBuilder.Append(';'); - stringBuilder.Append(ModInfo.IS_HOTFIX); + stringBuilder.Append(BAUPlugin.ModInfo.IS_HOTFIX); stringBuilder.Append('/'); - stringBuilder.Append(ModInfo.HOTFIX_NUM); + stringBuilder.Append(BAUPlugin.ModInfo.HOTFIX_NUM); stringBuilder.Append('/'); - stringBuilder.Append(ModInfo.BETA_NUM); + stringBuilder.Append(BAUPlugin.ModInfo.BETA_NUM); return stringBuilder.ToString(); } diff --git a/src/Utilities/Utils.cs b/src/Utilities/Utils.cs index 3198b21..7897aca 100644 --- a/src/Utilities/Utils.cs +++ b/src/Utilities/Utils.cs @@ -444,7 +444,7 @@ internal static class Utils { try { - var stream = ModInfo.Assembly.GetManifestResourceStream(path); + var stream = BAUPlugin.ModInfo.Assembly.GetManifestResourceStream(path); if (stream == null) return null; From 0a0e5ea98381c1336742da60f105094a3529167b Mon Sep 17 00:00:00 2001 From: D1GQ Date: Fri, 21 Aug 2026 17:29:34 -0500 Subject: [PATCH 04/19] Move to semversioning 2.0.0 --- src/BAUPlugin.ModInfo.cs | 50 ++++--- src/BAUPlugin.cs | 42 +----- src/BetterAmongUs.csproj | 7 +- src/BetterAmongUs.sln | 3 + src/Commands/DumpCommand.cs | 8 +- src/FodyWeavers.xml | 8 + src/FodyWeavers.xsd | 141 ++++++++++++++++++ src/MonoScripts/BetterPingTracker.cs | 2 +- src/Network/Configs/BAUUpdateData.cs | 79 ++-------- src/Network/Loaders/BAUUpdateLoader.cs | 6 +- src/Patches/Client/ClientPatch.cs | 4 +- src/Patches/Client/Discord/DiscordPatch.cs | 4 +- src/Patches/Client/VersionShowerPatch.cs | 2 +- .../Gameplay/Managers/HudManagerPatch.cs | 2 +- src/Patches/Unity/UnityWebRequestPatch.cs | 29 ++-- 15 files changed, 232 insertions(+), 155 deletions(-) create mode 100644 src/FodyWeavers.xml create mode 100644 src/FodyWeavers.xsd diff --git a/src/BAUPlugin.ModInfo.cs b/src/BAUPlugin.ModInfo.cs index f8dee4b..696f546 100644 --- a/src/BAUPlugin.ModInfo.cs +++ b/src/BAUPlugin.ModInfo.cs @@ -1,4 +1,4 @@ -using BetterAmongUs.Enums; +using Semver; using System.Reflection; namespace BetterAmongUs; @@ -11,9 +11,33 @@ internal partial class BAUPlugin internal static class ModInfo { /// - /// Gets the release type of the current build. + /// The base version number of the mod in MAJOR.MINOR.PATCH format. /// - internal static readonly ReleaseTypes ReleaseBuildType = ReleaseTypes.Release; + internal const string VERSION_NUMBER = "1.3.4"; + + /// + /// The beta release number. Increment this for each beta release. + /// + internal const string BETA_NUMBER = "0"; + + /// + /// Gets the full version string for the current build configuration. + /// +#if RELEASE + internal const string VERSION = VERSION_NUMBER; +#elif BETA + internal const string VERSION = $"{VERSION_NUMBER}-beta-{BETA_NUMBER}"; +#endif + + /// + /// Gets the full version string with a v prefix for the current build configuration. + /// + internal const string VERSION_STRING = "v" + VERSION; + + /// + /// Gets the parsed semantic version of the mod. + /// + internal static readonly SemVersion SemVersion = SemVersion.Parse(VERSION); /// /// Gets the Git commit hash from assembly metadata. @@ -25,21 +49,6 @@ internal partial class BAUPlugin /// public static string BuildDate = ThisAssembly.Metadata.BuildDate; - /// - /// The beta number for beta releases. - /// - internal const string BETA_NUM = "0"; - - /// - /// The hotfix number for hotfix releases. - /// - internal const string HOTFIX_NUM = "1"; - - /// - /// Indicates whether this is a hotfix release. - /// - internal const bool IS_HOTFIX = true; - /// /// The name of BAU. /// @@ -50,11 +59,6 @@ internal partial class BAUPlugin /// internal const string PLUGIN_GUID = "com.d1gq.betteramongus"; - /// - /// The version of BAU. - /// - internal const string PLUGIN_VERSION = "1.3.3"; - /// /// Gets the list of supported Among Us versions. /// diff --git a/src/BAUPlugin.cs b/src/BAUPlugin.cs index f2a3405..87db49c 100644 --- a/src/BAUPlugin.cs +++ b/src/BAUPlugin.cs @@ -7,7 +7,6 @@ using BetterAmongUs.Attributes; using BetterAmongUs.Data; using BetterAmongUs.Data.Config; using BetterAmongUs.Data.Json; -using BetterAmongUs.Enums; using BetterAmongUs.Managers; using BetterAmongUs.Modules; using BetterAmongUs.Modules.OptionItems; @@ -22,42 +21,10 @@ using UnityEngine.SceneManagement; namespace BetterAmongUs; -[BepInPlugin(ModInfo.PLUGIN_GUID, ModInfo.PLUGIN_NAME, ModInfo.PLUGIN_VERSION)] +[BepInPlugin(ModInfo.PLUGIN_GUID, ModInfo.PLUGIN_NAME, ModInfo.VERSION)] [BepInProcess(ModInfo.AmongUs.PROCESS_NAME)] internal partial class BAUPlugin : BasePlugin { - /// - /// Gets the formatted version text for display. - /// - /// Whether to use newline separation for additional info. - /// Formatted version string. - internal static string GetVersionText(bool newLine = false) - { - string text = string.Empty; - - string newLineText = newLine ? "\n" : " "; - - switch (ModInfo.ReleaseBuildType) - { - case ReleaseTypes.Release: - text = $"v{BetterAmongUsVersion}"; - break; - case ReleaseTypes.Beta: - text = $"v{BetterAmongUsVersion}{newLineText}Beta {ModInfo.BETA_NUM}"; - break; - case ReleaseTypes.Dev: - text = $"v{BetterAmongUsVersion}{newLineText}Dev {ModInfo.CommitHash}-{ModInfo.BuildDate}"; - break; - default: - break; - } - - if (ModInfo.IS_HOTFIX) - text += $"{newLineText}Hotfix {ModInfo.HOTFIX_NUM}"; - - return text; - } - /// /// Gets the BAUPlugin instance. /// @@ -68,11 +35,6 @@ internal partial class BAUPlugin : BasePlugin /// internal static Harmony Harmony { get; } = new Harmony(ModInfo.PLUGIN_GUID); - /// - /// Gets the BetterAmongUs version string. - /// - internal static string BetterAmongUsVersion => ModInfo.PLUGIN_VERSION; - /// /// Gets the application version string. /// @@ -170,7 +132,7 @@ internal partial class BAUPlugin : BasePlugin Logger_.Log("Better Among Us successfully loaded!"); string SupportedVersions = string.Join(" ", ModInfo.SupportedAmongUsVersions); - Logger_.Log($"BetterAmongUs {BetterAmongUsVersion}-{ModInfo.BuildDate} - [{AppVersion} --> {SupportedVersions}] {Utils.GetPlatformName(PlatformData.Platform)}"); + Logger_.Log($"BetterAmongUs {ModInfo.VERSION_STRING}-{ModInfo.BuildDate} - [{AppVersion} --> {SupportedVersions}] {Utils.GetPlatformName(PlatformData.Platform)}"); } /// diff --git a/src/BetterAmongUs.csproj b/src/BetterAmongUs.csproj index 344b17c..1dc25e9 100644 --- a/src/BetterAmongUs.csproj +++ b/src/BetterAmongUs.csproj @@ -6,6 +6,7 @@ true Library preview + Debug;Release;Beta @@ -85,15 +86,19 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + + runtime; compile; build; native; contentfiles; analyzers; buildtransitive + all + + all runtime; build; native; contentfiles; analyzers; buildtransitive - all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/BetterAmongUs.sln b/src/BetterAmongUs.sln index 6e51431..c2f354b 100644 --- a/src/BetterAmongUs.sln +++ b/src/BetterAmongUs.sln @@ -7,10 +7,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BetterAmongUs", "BetterAmon EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Beta|Any CPU = Beta|Any CPU Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {41193AE4-8181-49F8-9DA4-F4B7B275A0EA}.Beta|Any CPU.ActiveCfg = Beta|Any CPU + {41193AE4-8181-49F8-9DA4-F4B7B275A0EA}.Beta|Any CPU.Build.0 = Beta|Any CPU {41193AE4-8181-49F8-9DA4-F4B7B275A0EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {41193AE4-8181-49F8-9DA4-F4B7B275A0EA}.Debug|Any CPU.Build.0 = Debug|Any CPU {41193AE4-8181-49F8-9DA4-F4B7B275A0EA}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/src/Commands/DumpCommand.cs b/src/Commands/DumpCommand.cs index f6f9a01..f34c933 100644 --- a/src/Commands/DumpCommand.cs +++ b/src/Commands/DumpCommand.cs @@ -60,14 +60,14 @@ internal sealed class DumpCommand : BaseCommand { Directory.CreateDirectory(logFolderPath); } - string logFileName = "log-" + BAUPlugin.GetVersionText().Replace(' ', '-').ToLower() + "-" + DateTime.Now.ToString("yyyy.MM.dd-HH.mm.ss") + "-bau" + ".log"; + string logFileName = "log-" + BAUPlugin.ModInfo.VERSION_STRING + "-" + DateTime.Now.ToString("yyyy.MM.dd-HH.mm.ss") + "-bau" + ".log"; string logFile = Path.Combine(logFolderPath, logFileName); File.WriteAllText(logFile, newLog); string bepInExLog = Path.Combine(Paths.BepInExRootPath, "LogOutput.log"); if (File.Exists(bepInExLog)) { - string logFileBepInExeName = "log-" + BAUPlugin.GetVersionText().Replace(' ', '-').ToLower() + "-" + DateTime.Now.ToString("yyyy.MM.dd-HH.mm.ss") + "-bepinex" + ".log"; + string logFileBepInExeName = "log-" + BAUPlugin.ModInfo.VERSION_STRING + "-" + DateTime.Now.ToString("yyyy.MM.dd-HH.mm.ss") + "-bepinex" + ".log"; string logFileBepInExe = Path.Combine(logFolderPath, logFileBepInExeName); File.Copy(bepInExLog, logFileBepInExe); } @@ -93,14 +93,14 @@ internal sealed class DumpCommand : BaseCommand string androidLogContent = File.ReadAllText(logFilePath); string androidNewLog = DecryptLog(androidLogContent); - string androidLogFileName = "log-" + BAUPlugin.GetVersionText().Replace(' ', '-').ToLower() + "-" + DateTime.Now.ToString("yyyy.MM.dd-HH.mm.ss") + "-bau" + ".log"; + string androidLogFileName = "log-" + BAUPlugin.ModInfo.VERSION_STRING + "-" + DateTime.Now.ToString("yyyy.MM.dd-HH.mm.ss") + "-bau" + ".log"; string androidNewLogFilePath = Path.Combine(logFolderPath, androidLogFileName); File.WriteAllText(androidNewLogFilePath, androidNewLog); string bepInExLog = Path.Combine(Paths.BepInExRootPath, "LogOutput.log"); if (File.Exists(bepInExLog)) { - string logFileBepInExeName = "log-" + BAUPlugin.GetVersionText().Replace(' ', '-').ToLower() + "-" + DateTime.Now.ToString("yyyy.MM.dd-HH.mm.ss") + "-bepinex" + ".log"; + string logFileBepInExeName = "log-" + BAUPlugin.ModInfo.VERSION_STRING + "-" + DateTime.Now.ToString("yyyy.MM.dd-HH.mm.ss") + "-bepinex" + ".log"; string logFileBepInExe = Path.Combine(logFolderPath, logFileBepInExeName); File.Copy(bepInExLog, logFileBepInExe); } diff --git a/src/FodyWeavers.xml b/src/FodyWeavers.xml new file mode 100644 index 0000000..7ab88d8 --- /dev/null +++ b/src/FodyWeavers.xml @@ -0,0 +1,8 @@ + + + + + Semver + + + \ No newline at end of file diff --git a/src/FodyWeavers.xsd b/src/FodyWeavers.xsd new file mode 100644 index 0000000..05e92c1 --- /dev/null +++ b/src/FodyWeavers.xsd @@ -0,0 +1,141 @@ + + + + + + + + + + + + A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks + + + + + A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. + + + + + A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks + + + + + A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. + + + + + A list of unmanaged 32 bit assembly names to include, delimited with line breaks. + + + + + A list of unmanaged 64 bit assembly names to include, delimited with line breaks. + + + + + The order of preloaded assemblies, delimited with line breaks. + + + + + + This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file. + + + + + Controls if .pdbs for reference assemblies are also embedded. + + + + + Controls if runtime assemblies are also embedded. + + + + + Controls whether the runtime assemblies are embedded with their full path or only with their assembly name. + + + + + Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option. + + + + + As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off. + + + + + Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code. + + + + + Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior. + + + + + A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with | + + + + + A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |. + + + + + A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with | + + + + + A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with |. + + + + + A list of unmanaged 32 bit assembly names to include, delimited with |. + + + + + A list of unmanaged 64 bit assembly names to include, delimited with |. + + + + + The order of preloaded assemblies, delimited with |. + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/src/MonoScripts/BetterPingTracker.cs b/src/MonoScripts/BetterPingTracker.cs index 9137d68..aeace0b 100644 --- a/src/MonoScripts/BetterPingTracker.cs +++ b/src/MonoScripts/BetterPingTracker.cs @@ -84,7 +84,7 @@ internal sealed class BetterPingTracker : MonoBehaviour sb.AppendFormat("{0}: {1}\n", TranslationStrings.Timer.LocalizedString.ToUpper(), $"<{timeColor}>{lobbyTimerDisplay}"); } - sb.Append($"BetterAmongUs {BAUPlugin.GetVersionText(true)}\n"); + sb.Append($"BetterAmongUs {BAUPlugin.ModInfo.VERSION_STRING}\n"); sb.Append($"{BAUPlugin.ModInfo.GITHUB}\n".Size(52f)); if (BAUConfigs.ShowFPS.Value) diff --git a/src/Network/Configs/BAUUpdateData.cs b/src/Network/Configs/BAUUpdateData.cs index af87f85..291b1d8 100644 --- a/src/Network/Configs/BAUUpdateData.cs +++ b/src/Network/Configs/BAUUpdateData.cs @@ -1,5 +1,5 @@ -using BetterAmongUs.Enums; -using BetterAmongUs.Modules; +using BetterAmongUs.Modules; +using Semver; using System.Collections; using System.Text.Json.Serialization; using UnityEngine; @@ -12,6 +12,12 @@ namespace BetterAmongUs.Network.Configs; [Serializable] internal sealed class BAUUpdateData { + /// + /// Gets or sets the download link for the updated DLL file. + /// + [JsonPropertyName("valid")] + public bool Valid { get; set; } = false; + /// /// Gets or sets the download link for the updated DLL file. /// @@ -24,82 +30,23 @@ internal sealed class BAUUpdateData [JsonPropertyName("version")] public string Version { get; set; } = string.Empty; - /// - /// Gets or sets the release type as an integer value. - /// - [JsonPropertyName("releaseType")] - public int ReleaseType { get; set; } - - /// - /// Gets or sets whether this update is a hotfix. - /// - [JsonPropertyName("isHotfix")] - public bool IsHotfix { get; set; } - - /// - /// Gets or sets the hotfix number if this is a hotfix update. - /// - [JsonPropertyName("hotfixNumber")] - public int HotfixNumber { get; set; } - - /// - /// Gets or sets the beta number if this is a beta release. - /// - [JsonPropertyName("betaNumber")] - public int BetaNumber { get; set; } - /// /// Determines if this update is newer than the currently installed version. /// /// True if the update is newer, false otherwise. - /// - /// Compares version numbers, release types, and specific build numbers - /// to determine if an update should be applied. - /// internal bool IsNewUpdate() { try { - var updateVersion = new Version(Version); - var modVersion = new Version(BAUPlugin.ModInfo.PLUGIN_VERSION); - - // 1. Compare main version (major.minor.build) - if (updateVersion > modVersion) - { - return true; - } - else if (updateVersion < modVersion) + if (!Valid) { return false; } - // 2. Versions are equal, compare release types - var updateReleaseType = (ReleaseTypes)ReleaseType; - var currentReleaseType = BAUPlugin.ModInfo.ReleaseBuildType; + var updateVersion = SemVersion.Parse(Version); + var modVersion = BAUPlugin.ModInfo.SemVersion; - // Release is always preferred over Beta - if (updateReleaseType == ReleaseTypes.Release && currentReleaseType == ReleaseTypes.Beta) - return true; - - // Don't downgrade from Release to Beta - if (updateReleaseType == ReleaseTypes.Beta && currentReleaseType == ReleaseTypes.Release) - return false; - - // 3. Same version and release type, compare specific numbers - if (updateReleaseType == ReleaseTypes.Beta) - { - return BetaNumber > int.Parse(BAUPlugin.ModInfo.BETA_NUM); - } - - // Release version - check hotfixes - if (IsHotfix && !BAUPlugin.ModInfo.IS_HOTFIX) - return true; - - if (IsHotfix && BAUPlugin.ModInfo.IS_HOTFIX) - return HotfixNumber > int.Parse(BAUPlugin.ModInfo.HOTFIX_NUM); - - // Same version, same type, no newer hotfix - return false; + return updateVersion.ComparePrecedenceTo(modVersion) > 0; } catch (Exception ex) { @@ -152,6 +99,6 @@ internal sealed class BAUUpdateData /// A formatted string containing version information. public override string ToString() { - return $"{Version}:{ReleaseType}:{BetaNumber}:{HotfixNumber}"; + return $"{SemVersion.Parse(Version)}"; } } \ No newline at end of file diff --git a/src/Network/Loaders/BAUUpdateLoader.cs b/src/Network/Loaders/BAUUpdateLoader.cs index 194cf01..de9a25a 100644 --- a/src/Network/Loaders/BAUUpdateLoader.cs +++ b/src/Network/Loaders/BAUUpdateLoader.cs @@ -25,10 +25,6 @@ internal sealed class BAUUpdateLoader : MonoBehaviour /// Coroutine to fetch update data from the remote repository. /// /// IEnumerator for coroutine execution. - /// - /// If no internet connection is detected, it retries several times before giving up. - /// After successfully loading update data, it initializes the UpdateManager. - /// [HideFromIl2Cpp] internal IEnumerator CoFetchUpdateData() { @@ -47,7 +43,7 @@ internal sealed class BAUUpdateLoader : MonoBehaviour } string callBack = ""; - yield return GitHubFile.CoFetchTextFile(GitUrlPath.RepositoryApi.Combine("update.json").ToString(), (string text) => + yield return GitHubFile.CoFetchTextFile(GitUrlPath.RepositoryApi.Combine("update-V2.json").ToString(), (string text) => { callBack = text; }); diff --git a/src/Patches/Client/ClientPatch.cs b/src/Patches/Client/ClientPatch.cs index da3d86a..3f5f162 100644 --- a/src/Patches/Client/ClientPatch.cs +++ b/src/Patches/Client/ClientPatch.cs @@ -56,7 +56,7 @@ internal static class ClientPatch // Show warning popup for newer Among Us version Utils.ShowPopUp($"-= Warning =-\n\n" + - $"Better Among Us {BAUPlugin.GetVersionText()}\nsupports Among Us {verText},\n" + + $"Better Among Us {BAUPlugin.ModInfo.VERSION_STRING}\nsupports Among Us {verText},\n" + $"Among Us {BAUPlugin.AppVersion} is above the supported versions!\n" + $"You may encounter minor to game breaking bugs."); } @@ -71,7 +71,7 @@ internal static class ClientPatch // Show warning popup for older Among Us version Utils.ShowPopUp($"-= Warning =-\n\n" + - $"Better Among Us {BAUPlugin.GetVersionText()}\nsupports Among Us {verText},\n" + + $"Better Among Us {BAUPlugin.ModInfo.VERSION_STRING}\nsupports Among Us {verText},\n" + $"Among Us {BAUPlugin.AppVersion} is below the supported versions!\n" + $"You may encounter minor to game breaking bugs."); } diff --git a/src/Patches/Client/Discord/DiscordPatch.cs b/src/Patches/Client/Discord/DiscordPatch.cs index 895a98d..d93f9da 100644 --- a/src/Patches/Client/Discord/DiscordPatch.cs +++ b/src/Patches/Client/Discord/DiscordPatch.cs @@ -23,7 +23,7 @@ internal static class DiscordPatch if (activity == null) return; - string details = $"BAU {BAUPlugin.GetVersionText()}"; + string details = $"BAU {BAUPlugin.ModInfo.VERSION_STRING}"; activity.Details = details; // Skip lobby info processing if Discord already shows "In Menus" @@ -50,7 +50,7 @@ internal static class DiscordPatch { // Streamer mode hides lobby code, only show mode info if (GameState.IsHideNSeek) - details = $"BAU v{BAUPlugin.ModInfo.PLUGIN_VERSION} - Hide & Seek"; + details = $"BAU v{BAUPlugin.ModInfo.VERSION_STRING} - Hide & Seek"; } } catch diff --git a/src/Patches/Client/VersionShowerPatch.cs b/src/Patches/Client/VersionShowerPatch.cs index 9a10548..fdef30d 100644 --- a/src/Patches/Client/VersionShowerPatch.cs +++ b/src/Patches/Client/VersionShowerPatch.cs @@ -13,6 +13,6 @@ internal static class VersionShowerPatch { string mark = TranslationStrings.BAUMark.LocalizedString; string bau = TranslationStrings.BAU.LocalizedString; - __instance.text.text = $"{mark}{bau}{mark} {BAUPlugin.GetVersionText()} ~ {Utils.GetPlatformName(BAUPlugin.PlatformData.Platform)} v{BAUPlugin.AmongUsVersion} ({BAUPlugin.AppVersion})"; + __instance.text.text = $"{mark}{bau}{mark} {BAUPlugin.ModInfo.VERSION_STRING} ~ {Utils.GetPlatformName(BAUPlugin.PlatformData.Platform)} v{BAUPlugin.AmongUsVersion} ({BAUPlugin.AppVersion})"; } } diff --git a/src/Patches/Gameplay/Managers/HudManagerPatch.cs b/src/Patches/Gameplay/Managers/HudManagerPatch.cs index d5ae239..daf6449 100644 --- a/src/Patches/Gameplay/Managers/HudManagerPatch.cs +++ b/src/Patches/Gameplay/Managers/HudManagerPatch.cs @@ -13,7 +13,7 @@ namespace BetterAmongUs.Patches.Gameplay.Managers; [HarmonyPatch] internal static class HudManagerPatch { - internal static string WelcomeMessage => $"{TranslationStrings.WelcomeMsg_WelcomeToBAU.Format(TranslationStrings.BetterAmongUs)}\n{BAUPlugin.GetVersionText()}\n" + + internal static string WelcomeMessage => $"{TranslationStrings.WelcomeMsg_WelcomeToBAU.Format(TranslationStrings.BetterAmongUs)}\n{BAUPlugin.ModInfo.VERSION_STRING}\n" + $"{TranslationStrings.WelcomeMsg_ThanksForDownloading}\n \n" + TranslationStrings.WelcomeMsg_BAUDescription1.Format(TranslationStrings.BAU, TranslationStrings.BetterOption_AntiCheat); diff --git a/src/Patches/Unity/UnityWebRequestPatch.cs b/src/Patches/Unity/UnityWebRequestPatch.cs index cef2c01..deabc9d 100644 --- a/src/Patches/Unity/UnityWebRequestPatch.cs +++ b/src/Patches/Unity/UnityWebRequestPatch.cs @@ -10,21 +10,32 @@ namespace BetterAmongUs.Patches.Unity; [HarmonyPatch] internal static class UnityWebRequestPatch { - // Build mod version header string + /// + /// Builds a mod version header string for network transmission. + /// Format: "major;minor;patch;metadata1/metadata2/metadata3" + /// public static string GetHeader() { var stringBuilder = new StringBuilder(); + var version = BAUPlugin.ModInfo.SemVersion; - // Format: "Version;BuildType;IsHotfix/HotfixNum/BetaNum" - stringBuilder.Append(BAUPlugin.ModInfo.PLUGIN_VERSION); + stringBuilder.Append(version.Major); stringBuilder.Append(';'); - stringBuilder.Append(Enum.GetName(BAUPlugin.ModInfo.ReleaseBuildType)); + stringBuilder.Append(version.Minor); stringBuilder.Append(';'); - stringBuilder.Append(BAUPlugin.ModInfo.IS_HOTFIX); - stringBuilder.Append('/'); - stringBuilder.Append(BAUPlugin.ModInfo.HOTFIX_NUM); - stringBuilder.Append('/'); - stringBuilder.Append(BAUPlugin.ModInfo.BETA_NUM); + stringBuilder.Append(version.Patch); + stringBuilder.Append(';'); + + var buildMetadata = version.Metadata; + if (buildMetadata.Length > 0) + { + for (int i = 0; i < buildMetadata.Length; i++) + { + stringBuilder.Append(buildMetadata[i]); + if (i < buildMetadata.Length - 1) + stringBuilder.Append('/'); + } + } return stringBuilder.ToString(); } From 671db2ed45618c7acb197bb62b01a606bd4a30c0 Mon Sep 17 00:00:00 2001 From: D1GQ Date: Fri, 21 Aug 2026 18:53:58 -0500 Subject: [PATCH 05/19] Use id structs --- src/Data/Replay/Events/AppearReplayEvent.cs | 3 ++- src/Data/Replay/Events/ChatEvent.cs | 3 ++- src/Data/Replay/Events/ChatNoteEvent.cs | 3 ++- src/Data/Replay/Events/MurderReplayEvent.cs | 5 +++-- src/Data/Replay/Events/ProtectReplayEvent.cs | 5 +++-- src/Data/Replay/Events/ProtectedReplayEvent.cs | 3 ++- src/Data/Replay/Events/SetRoleEvent.cs | 3 ++- src/Data/Replay/Events/ShapeshiftReplayEvent.cs | 5 +++-- src/Data/Replay/Events/UpdateSystemReplayEvent.cs | 3 ++- src/Data/Replay/Events/VanishReplayEvent.cs | 3 ++- src/Data/Replay/Events/VentReplayEvent.cs | 3 ++- src/Modules/OptionItems/OptionPlayerItem.cs | 3 ++- src/MonoScripts/PlayerInfoDisplay.cs | 3 ++- src/Utilities/Utils.cs | 12 ++++++------ 14 files changed, 35 insertions(+), 22 deletions(-) diff --git a/src/Data/Replay/Events/AppearReplayEvent.cs b/src/Data/Replay/Events/AppearReplayEvent.cs index 8aaad9c..8b2e334 100644 --- a/src/Data/Replay/Events/AppearReplayEvent.cs +++ b/src/Data/Replay/Events/AppearReplayEvent.cs @@ -1,6 +1,7 @@ using AmongUs.GameOptions; using BetterAmongUs.Interfaces; using BetterAmongUs.Utilities; +using InnerNet; using System.Text.Json.Serialization; namespace BetterAmongUs.Data.Replay.Events; @@ -19,7 +20,7 @@ internal sealed class AppearReplayEvent : IReplayEvent { if (Value != -1) { - var player = Utils.PlayerFromPlayerId(Value); + var player = Utils.PlayerFromPlayerId((PlayerId)Value); if (player != null) return $"{player.GetPlayerNameAndColor()}"; else diff --git a/src/MonoScripts/PlayerInfoDisplay.cs b/src/MonoScripts/PlayerInfoDisplay.cs index 9c33aac..c09dbd4 100644 --- a/src/MonoScripts/PlayerInfoDisplay.cs +++ b/src/MonoScripts/PlayerInfoDisplay.cs @@ -10,6 +10,7 @@ using BetterAmongUs.Patches.Gameplay.UI.Settings; using BetterAmongUs.Structs; using BetterAmongUs.Utilities; using Il2CppInterop.Runtime.Attributes; +using InnerNet; using System.Text.RegularExpressions; using TMPro; using UnityEngine; @@ -217,7 +218,7 @@ internal class PlayerInfoDisplay : MonoBehaviour } else { - var targetData = Utils.PlayerDataFromPlayerId(_player.shapeshiftTargetPlayerId); + var targetData = Utils.PlayerDataFromPlayerId((PlayerId)_player.shapeshiftTargetPlayerId); if (targetData == null) return; diff --git a/src/Utilities/Utils.cs b/src/Utilities/Utils.cs index 7897aca..0780249 100644 --- a/src/Utilities/Utils.cs +++ b/src/Utilities/Utils.cs @@ -77,7 +77,7 @@ internal static class Utils /// /// The client ID to look up. /// The ClientData if found, null otherwise. - internal static ClientData? ClientFromClientId(int clientId) => + internal static ClientData? ClientFromClientId(ClientId clientId) => AmongUsClient.Instance.allClients.FirstOrDefaultIl2Cpp(cd => cd.Id == clientId); /// @@ -85,7 +85,7 @@ internal static class Utils /// /// The player ID to look up. /// The NetworkedPlayerInfo if found, null otherwise. - internal static NetworkedPlayerInfo? PlayerDataFromPlayerId(int playerId) => + internal static NetworkedPlayerInfo? PlayerDataFromPlayerId(PlayerId playerId) => GameData.Instance.AllPlayers.FirstOrDefaultIl2Cpp(data => data.PlayerId == playerId); /// @@ -93,7 +93,7 @@ internal static class Utils /// /// The client ID to look up. /// The NetworkedPlayerInfo if found, null otherwise. - internal static NetworkedPlayerInfo? PlayerDataFromClientId(int clientId) => + internal static NetworkedPlayerInfo? PlayerDataFromClientId(ClientId clientId) => GameData.Instance.AllPlayers.FirstOrDefaultIl2Cpp(data => data.ClientId == clientId); /// @@ -109,7 +109,7 @@ internal static class Utils /// /// The player ID to look up. /// The PlayerControl if found, null otherwise. - internal static PlayerControl? PlayerFromPlayerId(int playerId) => + internal static PlayerControl? PlayerFromPlayerId(PlayerId playerId) => BAUPlugin.AllPlayerControls.FirstOrDefault(player => player.PlayerId == playerId); /// @@ -117,7 +117,7 @@ internal static class Utils /// /// The client ID to look up. /// The PlayerControl if found, null otherwise. - internal static PlayerControl? PlayerFromClientId(int clientId) => + internal static PlayerControl? PlayerFromClientId(ClientId clientId) => BAUPlugin.AllPlayerControls.FirstOrDefault(player => player.GetClientId() == clientId); /// @@ -125,7 +125,7 @@ internal static class Utils /// /// The network ID to look up. /// The PlayerControl if found, null otherwise. - internal static PlayerControl? PlayerFromNetId(uint netId) => + internal static PlayerControl? PlayerFromNetId(NetId netId) => BAUPlugin.AllPlayerControls.FirstOrDefault(player => player.NetId == netId); // Chat functionality From 1aee8ba1cd7a0f41ad61990dfa567aa6900fcf10 Mon Sep 17 00:00:00 2001 From: D1GQ Date: Fri, 21 Aug 2026 18:57:42 -0500 Subject: [PATCH 06/19] clean up --- src/Data/Replay/Events/AppearReplayEvent.cs | 5 ++--- src/Data/Replay/Events/ChatEvent.cs | 5 ++--- src/Data/Replay/Events/ChatNoteEvent.cs | 5 ++--- src/Data/Replay/Events/MurderReplayEvent.cs | 7 +++---- src/Data/Replay/Events/ProtectReplayEvent.cs | 7 +++---- src/Data/Replay/Events/ProtectedReplayEvent.cs | 5 ++--- src/Data/Replay/Events/SetRoleEvent.cs | 5 ++--- src/Data/Replay/Events/ShapeshiftReplayEvent.cs | 7 +++---- src/Data/Replay/Events/UpdateSystemReplayEvent.cs | 5 ++--- src/Data/Replay/Events/VanishReplayEvent.cs | 5 ++--- src/Data/Replay/Events/VentReplayEvent.cs | 5 ++--- src/Modules/OptionItems/OptionPlayerItem.cs | 3 +-- src/MonoScripts/PlayerInfoDisplay.cs | 3 +-- 13 files changed, 27 insertions(+), 40 deletions(-) diff --git a/src/Data/Replay/Events/AppearReplayEvent.cs b/src/Data/Replay/Events/AppearReplayEvent.cs index 8b2e334..b270f20 100644 --- a/src/Data/Replay/Events/AppearReplayEvent.cs +++ b/src/Data/Replay/Events/AppearReplayEvent.cs @@ -1,7 +1,6 @@ using AmongUs.GameOptions; using BetterAmongUs.Interfaces; using BetterAmongUs.Utilities; -using InnerNet; using System.Text.Json.Serialization; namespace BetterAmongUs.Data.Replay.Events; @@ -20,7 +19,7 @@ internal sealed class AppearReplayEvent : IReplayEvent { if (Value != -1) { - var player = Utils.PlayerFromPlayerId((PlayerId)Value); + var player = Utils.PlayerFromPlayerId((byte)Value); if (player != null) return $"{player.GetPlayerNameAndColor()}"; else diff --git a/src/MonoScripts/PlayerInfoDisplay.cs b/src/MonoScripts/PlayerInfoDisplay.cs index c09dbd4..27d7c0e 100644 --- a/src/MonoScripts/PlayerInfoDisplay.cs +++ b/src/MonoScripts/PlayerInfoDisplay.cs @@ -10,7 +10,6 @@ using BetterAmongUs.Patches.Gameplay.UI.Settings; using BetterAmongUs.Structs; using BetterAmongUs.Utilities; using Il2CppInterop.Runtime.Attributes; -using InnerNet; using System.Text.RegularExpressions; using TMPro; using UnityEngine; @@ -218,7 +217,7 @@ internal class PlayerInfoDisplay : MonoBehaviour } else { - var targetData = Utils.PlayerDataFromPlayerId((PlayerId)_player.shapeshiftTargetPlayerId); + var targetData = Utils.PlayerDataFromPlayerId((byte)_player.shapeshiftTargetPlayerId); if (targetData == null) return; From 0cfd47e7f6cf02fe6f5ec690830a8f7aab037c89 Mon Sep 17 00:00:00 2001 From: D1GQ Date: Fri, 21 Aug 2026 19:42:22 -0500 Subject: [PATCH 07/19] Ignore Starlight platform when checking for platform spoof --- src/Enums/CustomPlatforms.cs | 12 ++++++++++++ src/Patches/Gameplay/Anticheat/PlatformSpoofPatch.cs | 6 ++++++ 2 files changed, 18 insertions(+) create mode 100644 src/Enums/CustomPlatforms.cs diff --git a/src/Enums/CustomPlatforms.cs b/src/Enums/CustomPlatforms.cs new file mode 100644 index 0000000..10a58e6 --- /dev/null +++ b/src/Enums/CustomPlatforms.cs @@ -0,0 +1,12 @@ +namespace BetterAmongUs.Enums; + +/// +/// Represents custom platform identifiers. +/// +internal enum CustomPlatforms +{ + /// + /// Starlight. + /// + Starlight = 112 +} \ No newline at end of file diff --git a/src/Patches/Gameplay/Anticheat/PlatformSpoofPatch.cs b/src/Patches/Gameplay/Anticheat/PlatformSpoofPatch.cs index de8d22a..f11a435 100644 --- a/src/Patches/Gameplay/Anticheat/PlatformSpoofPatch.cs +++ b/src/Patches/Gameplay/Anticheat/PlatformSpoofPatch.cs @@ -1,5 +1,6 @@ using BepInEx.Unity.IL2CPP.Utils; using BetterAmongUs.Data.Config; +using BetterAmongUs.Enums; using BetterAmongUs.Generated; using BetterAmongUs.Managers; using BetterAmongUs.Modules; @@ -40,6 +41,11 @@ internal static class PlatformSpoofPatch if (player != null && __instance != null) { + if (__instance.Platform == (Platforms)CustomPlatforms.Starlight) + { + yield break; + } + // Check Xbox/Windows store players for invalid platform ID length if (__instance.Platform is Platforms.StandaloneWin10 or Platforms.Xbox) { From a909ec65f21d1a9920bbc0cad243c58c69b2fda9 Mon Sep 17 00:00:00 2001 From: D1GQ Date: Wed, 26 Aug 2026 11:10:14 -0500 Subject: [PATCH 08/19] Clean up --- .../Gameplay/UI/Settings/GameSettingsPatch.cs | 9 ------- src/Resources/Lang/en_US.json | 5 ---- src/build/Generated/TranslationStrings.cs | 25 ------------------- 3 files changed, 39 deletions(-) diff --git a/src/Patches/Gameplay/UI/Settings/GameSettingsPatch.cs b/src/Patches/Gameplay/UI/Settings/GameSettingsPatch.cs index 907b5f7..8375ce2 100644 --- a/src/Patches/Gameplay/UI/Settings/GameSettingsPatch.cs +++ b/src/Patches/Gameplay/UI/Settings/GameSettingsPatch.cs @@ -29,8 +29,6 @@ internal sealed class BetterGameSettings internal static OptionCheckboxItem? RpcRateLimiting; internal static OptionIntItem? RpcRateLimit; - internal static OptionStringItem? RoleRandomizer; - internal static OptionCheckboxItem? CancelInvalidSabotage; internal static OptionCheckboxItem? CensorDetectionReason; } @@ -96,13 +94,6 @@ internal static class GameSettingsPatch BetterGameSettings.RpcRateLimit = OptionIntItem.Create(BetterSettingsTab, TranslationStrings.BetterSetting_Setting_RateLimit, (25, 1000, 1), 50, ("", " PS"), BetterGameSettings.RpcRateLimiting); } - // Role algorithm settings - if (IsPreload || GameState.IsHost) - { - OptionHeaderItem.Create(BetterSettingsTab, TranslationStrings.BetterSetting_MainHeader_RoleAlgorithm); - BetterGameSettings.RoleRandomizer = OptionStringItem.Create(BetterSettingsTab, TranslationStrings.BetterSetting_Setting_RoleRandomizer, [new("System.Random"), new("UnityEngine.Random")], 0); - } - BetterSettingsTab.UpdateVisuals(); } diff --git a/src/Resources/Lang/en_US.json b/src/Resources/Lang/en_US.json index 35f731a..11dcb20 100644 --- a/src/Resources/Lang/en_US.json +++ b/src/Resources/Lang/en_US.json @@ -73,9 +73,7 @@ "BetterSetting.SetTo": "Set To:", "BetterSetting.MainHeader.System": "System Settings", "BetterSetting.MainHeader.AntiCheat": "Anti-Cheat Settings", - "BetterSetting.MainHeader.RoleAlgorithm": "Role Algorithm Settings", "BetterSetting.MainHeader.Gameplay": "Gameplay Settings", - "BetterSetting.MainHeader.HideNSeek": "Hide & Seek Settings", "BetterSetting.TextHeader.HostOnly": "Host Only", "BetterSetting.TextHeader.Detections": "Detections", @@ -91,7 +89,6 @@ "BetterSetting.Setting.UseBanChatList": "Use ban chat list", "BetterSetting.Setting.UseBanChatListOnlyLobby": "Only in lobby", "BetterSetting.Setting.UseBanChatListBan": "Ban", - "BetterSetting.Setting.ShowRoleForClients": "Show role in name for clients", "BetterSetting.Setting.CensorDetectionReason": "Censor detection reason", "BetterSetting.Setting.DetectedLevel": "Detected player levels", "BetterSetting.Setting.DetectedLevelAbove": "Detected player level above", @@ -102,8 +99,6 @@ "BetterSetting.Setting.DetectInvalidRpcs": "Detect invalid Rpcs", "BetterSetting.Setting.RpcRateLimiting": "Rpc Rate Limiting", "BetterSetting.Setting.RateLimit": "Rate Limit", - "BetterSetting.Setting.RoleRandomizer": "Randomizer", - "BetterSetting.Setting.DesyncRoles": "Desync roles to other clients", "BetterSetting.Setting.DisableSabotagesForDead": "Disable sabotages for dead", "BetterSetting.Setting.DisableSabotages": "Disable sabotages", "BetterSetting.Setting.RemovePetOnDeath": "Remove pet on death", diff --git a/src/build/Generated/TranslationStrings.cs b/src/build/Generated/TranslationStrings.cs index 18bf97a..f53931c 100644 --- a/src/build/Generated/TranslationStrings.cs +++ b/src/build/Generated/TranslationStrings.cs @@ -363,21 +363,11 @@ public static class TranslationStrings /// public static readonly TranslationString BetterSetting_MainHeader_AntiCheat = new("BetterSetting.MainHeader.AntiCheat"); - /// - /// Base Translation: <color=#4f92ff>Role Algorithm Settings</color> - /// - public static readonly TranslationString BetterSetting_MainHeader_RoleAlgorithm = new("BetterSetting.MainHeader.RoleAlgorithm"); - /// /// Base Translation: <color=#d7d700>Gameplay Settings</color> /// public static readonly TranslationString BetterSetting_MainHeader_Gameplay = new("BetterSetting.MainHeader.Gameplay"); - /// - /// Base Translation: <color=#d7d700>Hide & Seek Settings</color> - /// - public static readonly TranslationString BetterSetting_MainHeader_HideNSeek = new("BetterSetting.MainHeader.HideNSeek"); - /// /// Base Translation: <color=#4f92ff>Host Only</color> /// @@ -448,11 +438,6 @@ public static class TranslationStrings /// public static readonly TranslationString BetterSetting_Setting_UseBanChatListBan = new("BetterSetting.Setting.UseBanChatListBan"); - /// - /// Base Translation: Show role in name for clients - /// - public static readonly TranslationString BetterSetting_Setting_ShowRoleForClients = new("BetterSetting.Setting.ShowRoleForClients"); - /// /// Base Translation: Censor detection reason /// @@ -503,16 +488,6 @@ public static class TranslationStrings /// public static readonly TranslationString BetterSetting_Setting_RateLimit = new("BetterSetting.Setting.RateLimit"); - /// - /// Base Translation: Randomizer - /// - public static readonly TranslationString BetterSetting_Setting_RoleRandomizer = new("BetterSetting.Setting.RoleRandomizer"); - - /// - /// Base Translation: Desync roles to other clients - /// - public static readonly TranslationString BetterSetting_Setting_DesyncRoles = new("BetterSetting.Setting.DesyncRoles"); - /// /// Base Translation: Disable sabotages for dead /// From 5e5a1c9cd12f9f2739a8e06f7a1722b09c5b6f0a Mon Sep 17 00:00:00 2001 From: D1GQ Date: Wed, 26 Aug 2026 11:15:25 -0500 Subject: [PATCH 09/19] Fix mod not loading due to issues with migrating data --- src/Data/BetterDataManager.cs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/Data/BetterDataManager.cs b/src/Data/BetterDataManager.cs index cec589c..c31bb7b 100644 --- a/src/Data/BetterDataManager.cs +++ b/src/Data/BetterDataManager.cs @@ -206,13 +206,26 @@ internal static class BetterDataManager { if (File.Exists(Files.settingsFilePath_Legacy)) { - BAUConfigs.SettingsPreset.Value = 1; - File.Move(Files.settingsFilePath_Legacy, Files.SettingsFilePath); + if (File.Exists(Files.SettingsFilePath)) + { + File.Delete(Files.settingsFilePath_Legacy); + } + else + { + File.Move(Files.settingsFilePath_Legacy, Files.SettingsFilePath); + } } if (File.Exists(Files.dataFilePath_Legacy)) { - File.Move(Files.dataFilePath_Legacy, Files.dataFilePath); + if (File.Exists(Files.dataFilePath)) + { + File.Delete(Files.dataFilePath_Legacy); + } + else + { + File.Move(Files.dataFilePath_Legacy, Files.dataFilePath); + } } HandleMigrationFromWildcardToRegex(); From 3b66d9018311e79f9250f221ca122ffb3c68d72d Mon Sep 17 00:00:00 2001 From: D1GQ Date: Thu, 27 Aug 2026 17:00:02 -0500 Subject: [PATCH 10/19] Fix null exceptions --- .../FindAGameManagerPatch.cs | 24 +++++++++++++------ src/Utilities/RoleUtils.cs | 2 +- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/Patches/Gameplay/Managers/FindAGameManagerClass/FindAGameManagerPatch.cs b/src/Patches/Gameplay/Managers/FindAGameManagerClass/FindAGameManagerPatch.cs index 7ea9b42..4c8ca4a 100644 --- a/src/Patches/Gameplay/Managers/FindAGameManagerClass/FindAGameManagerPatch.cs +++ b/src/Patches/Gameplay/Managers/FindAGameManagerClass/FindAGameManagerPatch.cs @@ -114,14 +114,24 @@ internal static class FindAGameManagerPatch Transform child = container.transform.Find("Container"); Transform tmproObject = child.Find("TrueHostName_TMP"); - // Use existing TMP component or create new one - TMP_Text tmpro = tmproObject != null - ? tmproObject.GetComponent() - : CreateNewTextMeshPro(child); + TMP_Text? tmpro = null; - tmpro.font = container.capacity.font; - tmpro.fontSize = 3f; - tmpro.text = FormatGameInfoText(container.gameListing); + if (tmproObject != null) + { + tmpro = tmproObject.GetComponent(); + } + + if (tmpro == null) + { + tmpro = CreateNewTextMeshPro(child); + } + + if (tmpro != null) + { + tmpro.font = container.capacity?.font; + tmpro.fontSize = 3f; + tmpro.text = FormatGameInfoText(container.gameListing); + } } } diff --git a/src/Utilities/RoleUtils.cs b/src/Utilities/RoleUtils.cs index 1aa33dd..2e134ab 100644 --- a/src/Utilities/RoleUtils.cs +++ b/src/Utilities/RoleUtils.cs @@ -80,7 +80,7 @@ internal static class RoleUtils return color; } - return string.Empty; + return "#ffffff"; } /// From 8bf66881ca9431af2de6dd4c2ee441c528fc5308 Mon Sep 17 00:00:00 2001 From: D1GQ Date: Thu, 27 Aug 2026 17:51:35 -0500 Subject: [PATCH 11/19] Add BetterMinimapColors and BetterColorblindText options --- src/Data/Config/BAUConfigs.cs | 10 ++++ src/MonoScripts/MeetingInfoDisplay.cs | 10 +++- src/MonoScripts/PlayerInfoDisplay.cs | 8 +++ .../Client/OptionsMenuBehaviourPatch.cs | 7 ++- .../Gameplay/Player/CosmeticsLayerPatch.cs | 21 ++++++- .../Gameplay/Player/PlayerControlPatch.cs | 2 +- .../Gameplay/UI/MiniMapBehaviourPatch.cs | 56 +++++++++++++++---- src/Resources/Lang/en_US.json | 2 + src/build/Generated/TranslationStrings.cs | 10 ++++ 9 files changed, 110 insertions(+), 16 deletions(-) diff --git a/src/Data/Config/BAUConfigs.cs b/src/Data/Config/BAUConfigs.cs index 7096c11..3e01577 100644 --- a/src/Data/Config/BAUConfigs.cs +++ b/src/Data/Config/BAUConfigs.cs @@ -63,11 +63,21 @@ internal static class BAUConfigs /// internal static BAUConfigEntry MinimapIcons { get; } = new("Better Options", "MinimapIcons", true); + /// + /// Gets the configuration entry for new minimap colors setting. + /// + internal static BAUConfigEntry BetterMinimapColors { get; } = new("Better Options", "BetterMinimapColors", true); + /// /// Gets the configuration entry for vent color groups setting. /// internal static BAUConfigEntry VentColorGroups { get; } = new("Better Options", "VentColorGroups", true); + /// + /// Gets the configuration entry for better color blind text setting. + /// + internal static BAUConfigEntry BetterColorblindText { get; } = new("Better Options", "BetterColorblindText", true); + /// /// Gets the configuration entry for compress settings file setting. /// diff --git a/src/MonoScripts/MeetingInfoDisplay.cs b/src/MonoScripts/MeetingInfoDisplay.cs index 489f86b..f4c84cb 100644 --- a/src/MonoScripts/MeetingInfoDisplay.cs +++ b/src/MonoScripts/MeetingInfoDisplay.cs @@ -1,5 +1,6 @@ using BetterAmongUs.Attributes; using BetterAmongUs.Data; +using BetterAmongUs.Data.Config; using BetterAmongUs.Generated; using BetterAmongUs.Modules.Support; using BetterAmongUs.MonoScripts.Extended; @@ -91,7 +92,14 @@ internal sealed class MeetingInfoDisplay : PlayerInfoDisplay if (!BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_CustomColorBlindText)) { - _pva.ColorBlindName.transform.localPosition = new Vector3(-0.91f, -0.19f, -0.05f); + if (BAUConfigs.BetterColorblindText.Value) + { + _pva.ColorBlindName.transform.localPosition = new Vector3(-0.91f, -0.19f, -0.05f); + } + else + { + _pva.ColorBlindName.transform.localPosition = _pva.NameText.transform.localPosition - new Vector3(0f, 0.19f, 0f); + } } _lastUpdateFrame = Time.frameCount; diff --git a/src/MonoScripts/PlayerInfoDisplay.cs b/src/MonoScripts/PlayerInfoDisplay.cs index 27d7c0e..fecb1b3 100644 --- a/src/MonoScripts/PlayerInfoDisplay.cs +++ b/src/MonoScripts/PlayerInfoDisplay.cs @@ -411,12 +411,20 @@ internal class PlayerInfoDisplay : MonoBehaviour private void UpdateColorBlindTextPosition() { if (BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_CustomColorBlindText)) + { return; + } var text = _player.cosmetics.colorBlindText; if (!text.enabled) return; + if (!BAUConfigs.BetterColorblindText.Value) + { + text.transform.localPosition = new Vector3(0f, -0.2f, 0f); + return; + } + if (!_player.onLadder && !_player.MyPhysics.Animations.IsPlayingAnyLadderAnimation()) { text.transform.localPosition = new Vector3(0f, -1.3f, 0.4999f); diff --git a/src/Patches/Client/OptionsMenuBehaviourPatch.cs b/src/Patches/Client/OptionsMenuBehaviourPatch.cs index c30e7b4..7d0c3bc 100644 --- a/src/Patches/Client/OptionsMenuBehaviourPatch.cs +++ b/src/Patches/Client/OptionsMenuBehaviourPatch.cs @@ -5,6 +5,7 @@ using BetterAmongUs.Generated; using BetterAmongUs.Managers; using BetterAmongUs.Modules; using BetterAmongUs.MonoScripts.Extended; +using BetterAmongUs.Patches.Gameplay.Player; using BetterAmongUs.Patches.Gameplay.UI; using BetterAmongUs.Patches.Gameplay.UI.Chat; using BetterAmongUs.Utilities; @@ -55,8 +56,10 @@ internal static class OptionsMenuBehaviourPatch ClientOptionItem.CreateToggle(TranslationStrings.BetterOption_UnlockFPS, BAUConfigs.UnlockFPS, 1, __instance, UpdateFrameRate); ClientOptionItem.CreateToggle(TranslationStrings.BetterOption_ShowFPS, BAUConfigs.ShowFPS, 1, __instance); - ClientOptionItem.CreateToggle(TranslationStrings.BetterOption_VentColorGroups, BAUConfigs.VentColorGroups, 2, __instance, MiniMapBehaviourPatch.ClearMapIcons); - ClientOptionItem.CreateToggle(TranslationStrings.BetterOption_MinimapIcons, BAUConfigs.MinimapIcons, 2, __instance, MiniMapBehaviourPatch.ClearMapIcons); + ClientOptionItem.CreateToggle(TranslationStrings.BetterOption_MinimapIcons, BAUConfigs.MinimapIcons, 2, __instance, MiniMapBehaviourPatch.ForceCloseMiniMap); + ClientOptionItem.CreateToggle(TranslationStrings.BetterOption_BetterMinimapColors, BAUConfigs.BetterMinimapColors, 2, __instance, MiniMapBehaviourPatch.ForceCloseMiniMap); + ClientOptionItem.CreateToggle(TranslationStrings.BetterOption_VentColorGroups, BAUConfigs.VentColorGroups, 2, __instance, MiniMapBehaviourPatch.ForceCloseMiniMap); + ClientOptionItem.CreateToggle(TranslationStrings.BetterOption_BetterColorblindText, BAUConfigs.BetterColorblindText, 2, __instance, CosmeticsLayerPatch.UpdateAllColorblindText); ClientOptionItem.CreateToggle(TranslationStrings.BetterOption_CompressSettingFiles, BAUConfigs.CompressSettingFiles, 2, __instance, ConvertAllSettingFiles); // Button options (no toggle) diff --git a/src/Patches/Gameplay/Player/CosmeticsLayerPatch.cs b/src/Patches/Gameplay/Player/CosmeticsLayerPatch.cs index 3e2097f..de196d3 100644 --- a/src/Patches/Gameplay/Player/CosmeticsLayerPatch.cs +++ b/src/Patches/Gameplay/Player/CosmeticsLayerPatch.cs @@ -1,5 +1,6 @@ -using BetterAmongUs.Utilities; +using BetterAmongUs.Data.Config; using BetterAmongUs.Modules.Support; +using BetterAmongUs.Utilities; using HarmonyLib; namespace BetterAmongUs.Patches.Gameplay.Player; @@ -7,11 +8,27 @@ namespace BetterAmongUs.Patches.Gameplay.Player; [HarmonyPatch] internal static class CosmeticsLayerPatch { + internal static void UpdateAllColorblindText() + { + foreach (var player in PlayerControl.AllPlayerControls) + { + player.cosmetics.colorBlindText.text = player.cosmetics.GetColorBlindText(); + } + + if (MeetingHud.Instance != null) + { + foreach (var pva in MeetingHud.Instance.playerStates) + { + pva.SetColorblindText(); + } + } + } + [HarmonyPatch(typeof(CosmeticsLayer), nameof(CosmeticsLayer.GetColorBlindText))] [HarmonyPrefix] private static bool CosmeticsLayer_GetColorBlindText_Prefix(CosmeticsLayer __instance, ref string __result) { - if (BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_CustomColorBlindText)) + if (!BAUConfigs.BetterColorblindText.Value || BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_CustomColorBlindText)) { return true; } diff --git a/src/Patches/Gameplay/Player/PlayerControlPatch.cs b/src/Patches/Gameplay/Player/PlayerControlPatch.cs index 9755099..825489a 100644 --- a/src/Patches/Gameplay/Player/PlayerControlPatch.cs +++ b/src/Patches/Gameplay/Player/PlayerControlPatch.cs @@ -104,7 +104,7 @@ internal static class PlayerControlPatch if (targetPlayer == null) return; - if (!BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_CustomColorBlindText)) + if (BAUConfigs.BetterColorblindText.Value && !BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_CustomColorBlindText)) { if (targetPlayer.Data.PlayerId == __instance.Data.PlayerId) { diff --git a/src/Patches/Gameplay/UI/MiniMapBehaviourPatch.cs b/src/Patches/Gameplay/UI/MiniMapBehaviourPatch.cs index e2ea37b..f23e150 100644 --- a/src/Patches/Gameplay/UI/MiniMapBehaviourPatch.cs +++ b/src/Patches/Gameplay/UI/MiniMapBehaviourPatch.cs @@ -20,39 +20,68 @@ internal static class MiniMapBehaviourPatch [HarmonyPatch(typeof(MapBehaviour), nameof(MapBehaviour.ShowNormalMap))] [HarmonyPostfix] private static void MapBehaviour_ShowNormalMap_Postfix(MapBehaviour __instance) - => __instance.ColorControl.SetColor(new Color(0.05f, 0.6f, 1f, 1f)); // Blue tint for normal map + { + if (!BAUConfigs.BetterMinimapColors.Value) + { + return; + } + + __instance.ColorControl.SetColor(new Color(0.05f, 0.6f, 1f, 1f)); + } [HarmonyPatch(typeof(MapBehaviour), nameof(MapBehaviour.ShowDetectiveMap))] [HarmonyPostfix] private static void MapBehaviour_ShowDetectiveMap_Postfix(MapBehaviour __instance) - => __instance.ColorControl.SetColor(new Color(0.05f, 0.6f, 1f, 1f)); // Blue tint for detective map + { + if (!BAUConfigs.BetterMinimapColors.Value) + { + return; + } + + __instance.ColorControl.SetColor(new Color(0.05f, 0.6f, 1f, 1f)); + } [HarmonyPatch(typeof(MapBehaviour), nameof(MapBehaviour.ShowSabotageMap))] [HarmonyPostfix] private static void MapBehaviour_ShowSabotageMap_Postfix(MapBehaviour __instance) - => __instance.ColorControl.SetColor(new Color(1f, 0.3f, 0f, 1f)); // Orange tint for sabotage map + { + if (!BAUConfigs.BetterMinimapColors.Value) + { + return; + } + + __instance.ColorControl.SetColor(new Color(1f, 0.3f, 0f, 1f)); + } [HarmonyPatch(typeof(MapCountOverlay), nameof(MapCountOverlay.OnEnable))] [HarmonyPostfix] private static void MapCountOverlay_OnEnable_Postfix(MapCountOverlay __instance) - // Green background normally, gray if comms sabotaged - => __instance.BackgroundColor.SetColor(PlayerTask.PlayerHasTaskOfType(PlayerControl.LocalPlayer) ? Palette.DisabledGrey : new Color(0.2f, 0.5f, 0f, 1f)); + { + if (!BAUConfigs.BetterMinimapColors.Value) + { + return; + } + + __instance.BackgroundColor.SetColor(PlayerTask.PlayerHasTaskOfType(PlayerControl.LocalPlayer) ? Palette.DisabledGrey : new Color(0.2f, 0.5f, 0f, 1f)); + } [HarmonyPatch(typeof(MapCountOverlay), nameof(MapCountOverlay.Update))] [HarmonyPrefix] private static void MapCountOverlay_Update_Prefix(MapCountOverlay __instance) { - // Handle comms sabotage effect on map + if (!BAUConfigs.BetterMinimapColors.Value) + { + return; + } + if (PlayerTask.PlayerHasTaskOfType(PlayerControl.LocalPlayer)) { - // Comms sabotaged - disable map __instance.isSab = true; __instance.BackgroundColor.SetColor(Palette.DisabledGrey); __instance.SabotageText.gameObject.SetActive(true); } else { - // Comms working - normal map __instance.isSab = false; __instance.BackgroundColor.SetColor(new Color(0.2f, 0.5f, 0f, 1f)); __instance.SabotageText.gameObject.SetActive(false); @@ -62,14 +91,21 @@ internal static class MiniMapBehaviourPatch [HarmonyPatch(typeof(MapConsole), nameof(MapConsole.Use))] [HarmonyPostfix] private static void MapConsole_ShowCountOverlay_Postfix() - => MapBehaviour.Instance.ColorControl.SetColor(new Color(0.2f, 0.5f, 0f, 1f)); // Green tint for admin map + { + if (!BAUConfigs.BetterMinimapColors.Value) + { + return; + } + + MapBehaviour.Instance.ColorControl.SetColor(new Color(0.2f, 0.5f, 0f, 1f)); + } private static Transform? _icons; // Container for all custom map icons /// /// Clears all map icons to be regenerated /// - internal static void ClearMapIcons() + internal static void ForceCloseMiniMap() { if (_icons != null) { diff --git a/src/Resources/Lang/en_US.json b/src/Resources/Lang/en_US.json index 11dcb20..1c7c8f6 100644 --- a/src/Resources/Lang/en_US.json +++ b/src/Resources/Lang/en_US.json @@ -63,6 +63,8 @@ "BetterOption.ShowFPS": "ShowFPS", "BetterOption.VentColorGroups": "Vent Color Groups", "BetterOption.MinimapIcons": "Minimap Icons", + "BetterOption.BetterMinimapColors": "Better Minimap Colors", + "BetterOption.BetterColorblindText": "Better Colorblind Text", "BetterOption.CompressSettingFiles": "Compress Setting Files", "BetterOption.SaveData": "Open Save Data", diff --git a/src/build/Generated/TranslationStrings.cs b/src/build/Generated/TranslationStrings.cs index f53931c..43a0c8c 100644 --- a/src/build/Generated/TranslationStrings.cs +++ b/src/build/Generated/TranslationStrings.cs @@ -323,6 +323,16 @@ public static class TranslationStrings /// public static readonly TranslationString BetterOption_MinimapIcons = new("BetterOption.MinimapIcons"); + /// + /// Base Translation: Better Minimap Colors + /// + public static readonly TranslationString BetterOption_BetterMinimapColors = new("BetterOption.BetterMinimapColors"); + + /// + /// Base Translation: Better Colorblind Text + /// + public static readonly TranslationString BetterOption_BetterColorblindText = new("BetterOption.BetterColorblindText"); + /// /// Base Translation: Compress Setting Files /// From 145b9baec0708b0ba1f859557f0dd8d226b33626 Mon Sep 17 00:00:00 2001 From: D1GQ Date: Fri, 28 Aug 2026 15:20:50 -0500 Subject: [PATCH 12/19] Fix chat theme not being applied to new Ghost messages --- src/Patches/Gameplay/UI/Chat/ChatPatch.cs | 33 +++++++++++------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/Patches/Gameplay/UI/Chat/ChatPatch.cs b/src/Patches/Gameplay/UI/Chat/ChatPatch.cs index 7c970f1..ecfec92 100644 --- a/src/Patches/Gameplay/UI/Chat/ChatPatch.cs +++ b/src/Patches/Gameplay/UI/Chat/ChatPatch.cs @@ -8,7 +8,6 @@ using BetterAmongUs.Structs; using BetterAmongUs.Utilities; using BetterAmongUs.Utilities.Extension; using HarmonyLib; -using TMPro; using UnityEngine; namespace BetterAmongUs.Patches.Gameplay.UI.Chat; @@ -294,9 +293,16 @@ internal static class ChatPatch bubble.NameText.SetText(playerName); } + [HarmonyPatch(typeof(ChatBubble), nameof(ChatBubble.SetName))] + [HarmonyPostfix] + private static void ChatBubble_SetName_Postfix(ChatBubble __instance) + { + SetChatPoolTheme(__instance); + } + [HarmonyPatch(typeof(ChatController), nameof(ChatController.GetPooledBubble))] [HarmonyPostfix] - private static void ChatController_GetPooledBubble_Postfix(ChatController __instance, ChatBubble __result) + private static void ChatController_GetPooledBubble_Postfix(ChatBubble __result) { SetChatPoolTheme(__result); } @@ -323,38 +329,29 @@ internal static class ChatPatch chat.quickChatButton.transform.Find("QuickChatIcon").GetComponent().color = new Color(1f, 1f, 1f, 1f); chat.openKeyboardButton.transform.Find("OpenKeyboardIcon").GetComponent().color = new Color(1f, 1f, 1f, 1f); } + // Apply theme to all existing chat bubbles foreach (var item in HudManager.Instance.Chat.chatBubblePool.activeChildren.SelectIl2Cpp(c => c.GetComponent())) { SetChatPoolTheme(item); } } + // Apply theme to individual chat bubble internal static ChatBubble SetChatPoolTheme(ChatBubble asChatBubble) { var chatBubble = asChatBubble; + var alpha = chatBubble.Background.color.a; if (BAUConfigs.ChatDarkMode.Value) { - chatBubble.transform.Find("ChatText (TMP)").GetComponentInChildren(true).color = Color.white; - chatBubble.transform.Find("Background").GetComponentInChildren(true).color = new Color(0.15f, 0.15f, 0.15f, 1f); - - var mark = chatBubble.transform.Find("PoolablePlayer/xMark"); - if (mark != null && mark.GetComponentInChildren(true).enabled) - { - chatBubble.transform.Find("Background").GetComponentInChildren(true).color = new Color(0.15f, 0.15f, 0.15f, 0.5f); - } + chatBubble.TextArea.color = Color.white; + chatBubble.Background.color = new Color(0.15f, 0.15f, 0.15f, alpha); } else { - chatBubble.transform.Find("ChatText (TMP)").GetComponentInChildren(true).color = Color.black; - chatBubble.transform.Find("Background").GetComponentInChildren(true).color = Color.white; - - var mark = chatBubble.transform.Find("PoolablePlayer/xMark"); - if (mark != null && mark.GetComponentInChildren(true).enabled) - { - chatBubble.transform.Find("Background").GetComponentInChildren(true).color = new Color(1f, 1f, 1f, 0.5f); - } + chatBubble.TextArea.color = new Color(0.15f, 0.15f, 0.15f); + chatBubble.Background.color = new Color(1f, 1f, 1f, alpha); } return chatBubble; From 48b50ba20a045301add161123c38cfe3d3e49ff8 Mon Sep 17 00:00:00 2001 From: D1GQ Date: Fri, 28 Aug 2026 15:29:32 -0500 Subject: [PATCH 13/19] List Starlight as platform --- src/Utilities/Utils.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Utilities/Utils.cs b/src/Utilities/Utils.cs index 0780249..759d89a 100644 --- a/src/Utilities/Utils.cs +++ b/src/Utilities/Utils.cs @@ -1,4 +1,5 @@ using BepInEx.Unity.IL2CPP.Utils; +using BetterAmongUs.Enums; using BetterAmongUs.Generated; using BetterAmongUs.Modules; using BetterAmongUs.Patches.Gameplay.UI.Chat; @@ -602,6 +603,7 @@ internal static class Utils Platforms.Switch => ("Switch", "Console"), Platforms.Android => ("Android", "Mobile"), Platforms.IPhone => ("IPhone", "Mobile"), + (Platforms)CustomPlatforms.Starlight => ("Starlight", "Mobile"), Platforms.Unknown => ("None", ""), _ => (string.Empty, string.Empty) }; From 06ab2542c0b2552afd29ebcc61d1f673dd21c506 Mon Sep 17 00:00:00 2001 From: D1GQ Date: Fri, 28 Aug 2026 17:48:35 -0500 Subject: [PATCH 14/19] Make PlayerInfo command use NetworkedPlayerInfo --- src/Commands/Arguments/PlayerArgument.cs | 4 -- src/Commands/Arguments/PlayerInfoArgument.cs | 76 ++++++++++++++++++++ src/Commands/PlayerInfoCommand.cs | 24 ++++--- src/Commands/RemovePlayerCommand.cs | 4 +- 4 files changed, 91 insertions(+), 17 deletions(-) create mode 100644 src/Commands/Arguments/PlayerInfoArgument.cs diff --git a/src/Commands/Arguments/PlayerArgument.cs b/src/Commands/Arguments/PlayerArgument.cs index 0f32627..e6d4503 100644 --- a/src/Commands/Arguments/PlayerArgument.cs +++ b/src/Commands/Arguments/PlayerArgument.cs @@ -36,8 +36,6 @@ internal sealed class PlayerArgument(BaseCommand command, string argInfo = "{pla /// /// Tries to parse the player argument and find the corresponding PlayerControl. /// - /// - /// internal override bool TryParse(out PlayerControl result) { foreach (var player in BAUPlugin.AllPlayerControls) @@ -59,8 +57,6 @@ internal sealed class PlayerArgument(BaseCommand command, string argInfo = "{pla /// /// Checks if the given player info matches the argument value. /// - /// - /// private bool IsMatchingPlayer(NetworkedPlayerInfo data) { return Arg.Equals(data.PlayerName.Replace(' ', '_'), StringComparison.OrdinalIgnoreCase) diff --git a/src/Commands/Arguments/PlayerInfoArgument.cs b/src/Commands/Arguments/PlayerInfoArgument.cs new file mode 100644 index 0000000..0d66403 --- /dev/null +++ b/src/Commands/Arguments/PlayerInfoArgument.cs @@ -0,0 +1,76 @@ +using BetterAmongUs.Utilities; + +namespace BetterAmongUs.Commands.Arguments; + +/// +/// Represents a player command argument. +/// +/// The command this argument belongs to. +/// Information about the argument (default: "{player}"). +internal sealed class PlayerInfoArgument(BaseCommand command, string argInfo = "{player}") : BaseArgument(command, argInfo) +{ + protected override string[] GetArgSuggestions() + { + List suggestions = []; + var allPlayers = GameData.Instance.AllPlayers; + int count = allPlayers.Count; + + for (int i = 0; i < count; i++) + { + var playerData = allPlayers[i]; + if (playerData.IsLocalData()) + { + int insertIndex = suggestions.Count; + + if (!string.IsNullOrEmpty(playerData.PlayerName)) + suggestions.Insert(insertIndex, playerData.PlayerName.Replace(' ', '_')); + + if (!string.IsNullOrEmpty(playerData.FriendCode)) + suggestions.Insert(insertIndex + 1, playerData.FriendCode); + + suggestions.Insert(insertIndex + 2, $"ID{playerData.PlayerId}"); + } + else + { + if (!string.IsNullOrEmpty(playerData.PlayerName)) + suggestions.Add(playerData.PlayerName.Replace(' ', '_')); + + if (!string.IsNullOrEmpty(playerData.FriendCode)) + suggestions.Add(playerData.FriendCode); + + suggestions.Add($"ID{playerData.PlayerId}"); + } + } + + return [.. suggestions]; + } + + /// + /// Tries to parse the player argument and find the corresponding PlayerControl. + /// + internal override bool TryParse(out NetworkedPlayerInfo result) + { + foreach (var playerData in GameData.Instance.AllPlayers) + { + if (IsMatchingPlayer(playerData)) + { + result = playerData; + return true; + } + } + + result = default!; + BaseCommand.CommandErrorText("Player not found!"); + return false; + } + + /// + /// Checks if the given player info matches the argument value. + /// + private bool IsMatchingPlayer(NetworkedPlayerInfo data) + { + return Arg.Equals(data.PlayerName.Replace(' ', '_'), StringComparison.OrdinalIgnoreCase) + || Arg.Equals(data.FriendCode, StringComparison.OrdinalIgnoreCase) + || Arg.Equals($"ID{data.PlayerId}", StringComparison.OrdinalIgnoreCase); + } +} \ No newline at end of file diff --git a/src/Commands/PlayerInfoCommand.cs b/src/Commands/PlayerInfoCommand.cs index 3b0cd1c..ce9a0d5 100644 --- a/src/Commands/PlayerInfoCommand.cs +++ b/src/Commands/PlayerInfoCommand.cs @@ -13,29 +13,33 @@ internal sealed class PlayerInfoCommand : BaseCommand public PlayerInfoCommand() { - _playerArgument = new PlayerArgument(this); + _playerArgument = new PlayerInfoArgument(this); Arguments = [_playerArgument]; } - private readonly PlayerArgument _playerArgument; + private readonly PlayerInfoArgument _playerArgument; internal override void Run() { - if (!_playerArgument.TryParse(out var player)) + if (!_playerArgument.TryParse(out var playerData)) return; - if (player.Data == null) + if (playerData == null) return; StringBuilder sb = new(); - var hexColor = Utils.Color32ToHex(Palette.PlayerColors[player.CurrentOutfit.ColorId]); + var hexColor = Utils.Color32ToHex(Palette.PlayerColors[playerData.DefaultOutfit.ColorId]); var format1 = "┌ •"; var format2 = "├ •"; var format3 = "└ •"; - sb.Append($"{player.Data.PlayerName} Info:\n"); - sb.Append($"{format1} ID: {player.Data.PlayerId}\n"); - sb.Append($"{format2} HashPUID: {Utils.GetHashStr($"{player.Data.Puid}")}\n"); - sb.Append($"{format2} Platform: {Utils.GetPlatformName(player)}\n"); - sb.Append($"{format3} FriendCode: {player.Data.FriendCode}"); + sb.Append($"{playerData.PlayerName} Info:\n"); + sb.Append($"{format1} ID: {playerData.PlayerId}\n"); + sb.Append($"{format2} HashPUID: {Utils.GetHashStr($"{playerData.Puid}")}\n"); + var client = Utils.ClientFromClientId(playerData.ClientId); + if (client != null) + { + sb.Append($"{format2} Platform: {Utils.GetPlatformName(client.PlatformData.Platform)}\n"); + } + sb.Append($"{format3} FriendCode: {playerData.FriendCode}"); CommandResultText(sb.ToString()); } } diff --git a/src/Commands/RemovePlayerCommand.cs b/src/Commands/RemovePlayerCommand.cs index 34a83e0..5b49089 100644 --- a/src/Commands/RemovePlayerCommand.cs +++ b/src/Commands/RemovePlayerCommand.cs @@ -16,9 +16,7 @@ internal sealed class RemovePlayerCommand : BaseCommand _identifierArgument = new StringArgument(this, "{identifier}") { ArgSuggestions = () => - BetterDataManager.Files.BetterDataFile.AllCheatData - .SelectMany(info => new[] { info.HashPuid.Replace(' ', '_'), info.FriendCode.Replace(' ', '_'), info.PlayerName.Replace(' ', '_') }) - .ToArray() + [.. BetterDataManager.Files.BetterDataFile.AllCheatData.SelectMany(info => new[] { info.HashPuid.Replace(' ', '_'), info.FriendCode.Replace(' ', '_'), info.PlayerName.Replace(' ', '_') })] }; Arguments = [_identifierArgument]; } From c8410a811a6247a8122dd982bf3646db9cf29233 Mon Sep 17 00:00:00 2001 From: D1GQ Date: Sat, 29 Aug 2026 12:17:51 -0500 Subject: [PATCH 15/19] Only add chat messages to private logs --- src/Patches/Gameplay/UI/Chat/ChatPatch.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/Patches/Gameplay/UI/Chat/ChatPatch.cs b/src/Patches/Gameplay/UI/Chat/ChatPatch.cs index ecfec92..713a1fa 100644 --- a/src/Patches/Gameplay/UI/Chat/ChatPatch.cs +++ b/src/Patches/Gameplay/UI/Chat/ChatPatch.cs @@ -217,14 +217,7 @@ internal static class ChatPatch private static void ChatController_AddChat_Postfix(ChatController __instance, PlayerControl sourcePlayer, string chatText) { // Log chat publicly if player is alive, privately if dead - if (sourcePlayer.IsAlive() || !PlayerControl.LocalPlayer.IsAlive()) - { - Logger_.Log($"{sourcePlayer.Data.PlayerName} -> {chatText}", "ChatLog"); - } - else - { - Logger_.LogPrivate($"{sourcePlayer.Data.PlayerName} -> {chatText}", "ChatLog"); - } + Logger_.LogPrivate($"{sourcePlayer.Data.PlayerName} -> {chatText}", "ChatLog"); } [HarmonyPatch(typeof(ChatController), nameof(ChatController.SetChatBubbleName))] From 1f7c6d571d2eaf7bfc282519065dc22c78addd24 Mon Sep 17 00:00:00 2001 From: D1GQ Date: Sat, 29 Aug 2026 13:01:24 -0500 Subject: [PATCH 16/19] Rwork logging --- src/Attributes/RegisterInIl2Cpp.cs | 5 +- src/BAUPlugin.Constants.cs | 2 + src/BAUPlugin.cs | 23 +- src/Commands/DumpCommand.cs | 72 ++---- src/Data/BetterDataManager.cs | 14 +- src/Data/Json/AbstractJsonFile.cs | 4 +- src/Data/Json/BetterGameSettingsFile.cs | 2 +- src/Data/SharedSecretExchange.cs | 4 +- src/Managers/BetterNotificationManager.cs | 4 +- src/Managers/NetworkManager.cs | 6 +- src/Modules/AntiCheat/BetterAntiCheat.cs | 8 +- src/Modules/AntiCheat/RPCHandler.cs | 4 +- .../RPCHandlers/Cheats/AUMChatHandler.cs | 2 +- .../AntiCheat/RPCHandlers/CheckNameHandler.cs | 2 +- .../AntiCheat/RPCHandlers/SetNameHandler.cs | 2 +- src/Modules/BAULogger.cs | 214 ++++++++++++++++ src/Modules/Encryptor.cs | 80 +++++- src/Modules/Logger.cs | 240 ------------------ src/Modules/Support/BAUSupportEvent.cs | 2 +- src/Modules/Translator.cs | 16 +- src/MonoScripts/BetterPingTracker.cs | 2 +- .../Extended/ExtendedPlayerInfo.cs | 4 +- src/Network/Configs/BAUUpdateData.cs | 2 +- src/Network/Configs/NewsData.cs | 2 +- src/Network/GitHubFile.cs | 6 +- src/Network/Loaders/BAUUpdateLoader.cs | 2 +- src/Network/Loaders/NewsLoader.cs | 8 +- src/Patches/Client/ClientPatch.cs | 2 +- .../Gameplay/Anticheat/PlatformSpoofPatch.cs | 4 +- .../Gameplay/Anticheat/VoteBanSystemPatch.cs | 2 +- src/Patches/Gameplay/LobbyPatch.cs | 2 +- .../Gameplay/Player/PlayerControlPatch.cs | 16 +- .../Gameplay/Player/PlayerJoinAndLeftPatch.cs | 6 +- src/Patches/Gameplay/UI/Chat/ChatPatch.cs | 2 +- src/Patches/Gameplay/UI/GameSummaryPatch.cs | 10 +- src/Patches/Gameplay/UI/MeetingHudPatch.cs | 4 +- src/Patches/Unity/UnityWebRequestPatch.cs | 2 +- src/Utilities/Utils.cs | 10 +- 38 files changed, 417 insertions(+), 375 deletions(-) create mode 100644 src/Modules/BAULogger.cs delete mode 100644 src/Modules/Logger.cs diff --git a/src/Attributes/RegisterInIl2Cpp.cs b/src/Attributes/RegisterInIl2Cpp.cs index 3d854d8..e2b42e3 100644 --- a/src/Attributes/RegisterInIl2Cpp.cs +++ b/src/Attributes/RegisterInIl2Cpp.cs @@ -1,5 +1,4 @@ -using BetterAmongUs.Modules; -using Il2CppInterop.Runtime.Injection; +using Il2CppInterop.Runtime.Injection; using System.Reflection; namespace BetterAmongUs.Attributes; @@ -37,7 +36,7 @@ internal class RegisterInIl2Cpp(params Type[] interfaces) : Attribute } catch (Exception ex) { - Logger_.Error($"Failed to register {type.Name}: {ex.Message}"); + BAUPlugin.Logger.Error($"Failed to register {type.Name}: {ex.Message}"); } } } diff --git a/src/BAUPlugin.Constants.cs b/src/BAUPlugin.Constants.cs index 3cd98a2..7ba00ef 100644 --- a/src/BAUPlugin.Constants.cs +++ b/src/BAUPlugin.Constants.cs @@ -68,6 +68,8 @@ internal partial class BAUPlugin // **Cheat** """; + internal const char ENCRYPTED_LOG_PREFIX = '\u2063'; + internal const char ENCRYPTED_LOG_POSTFIX = '\u2064'; internal const int MAX_CHAT_TEXT = 120; internal const string BAU_CUSTOM_RPC_FLAG = "bau:rpc"; internal const string BAU_MODDED_PROTOCOL_FLAG = "bau:flags"; diff --git a/src/BAUPlugin.cs b/src/BAUPlugin.cs index 87db49c..4cd968a 100644 --- a/src/BAUPlugin.cs +++ b/src/BAUPlugin.cs @@ -70,10 +70,15 @@ internal partial class BAUPlugin : BasePlugin /// internal static Vent[] AllVents => UnityEngine.Object.FindObjectsOfType(); + /// + /// Gets the BAU logger instance. + /// + internal static BAULogger Logger { get; private set; } = null!; + /// /// Gets the BepInEx logger instance. /// - internal static ManualLogSource? Logger; + private static ManualLogSource? _manualLogSource; public override void Load() { @@ -100,7 +105,7 @@ internal partial class BAUPlugin : BasePlugin } catch (Exception ex) { - Logger_.Error(ex); + Logger.Error(ex); } } @@ -125,14 +130,10 @@ internal partial class BAUPlugin : BasePlugin OutfitData.Initialize(); SceneManager.add_sceneLoaded((Action)OnSceneLoaded); - if (File.Exists(BetterDataManager.Files.logFilePath)) - File.WriteAllText(BetterDataManager.Files.previousLogFilePath, File.ReadAllText(BetterDataManager.Files.logFilePath)); - - File.WriteAllText(BetterDataManager.Files.logFilePath, ""); - Logger_.Log("Better Among Us successfully loaded!"); + Logger.Log("Better Among Us successfully loaded!"); string SupportedVersions = string.Join(" ", ModInfo.SupportedAmongUsVersions); - Logger_.Log($"BetterAmongUs {ModInfo.VERSION_STRING}-{ModInfo.BuildDate} - [{AppVersion} --> {SupportedVersions}] {Utils.GetPlatformName(PlatformData.Platform)}"); + Logger.Log($"BetterAmongUs {ModInfo.VERSION_STRING}-{ModInfo.BuildDate} - [{AppVersion} --> {SupportedVersions}] {Utils.GetPlatformName(PlatformData.Platform)}"); } /// @@ -164,13 +165,15 @@ internal partial class BAUPlugin : BasePlugin /// private static void SetupConsole() { + Encryptor.Initialize(); ConsoleManager.CreateConsole(); ConsoleManager.ConfigPreventClose.Value = true; if (ConsoleManager.ConfigConsoleEnabled.Value) ConsoleManager.DetachConsole(); ConsoleManager.ConfigConsoleEnabled.Value = false; ConsoleManager.SetConsoleTitle("Among Us - BAU Console"); - Logger = BepInEx.Logging.Logger.CreateLogSource(ModInfo.PLUGIN_GUID); - var customLogListener = new CustomLogListener(); + _manualLogSource = BepInEx.Logging.Logger.CreateLogSource(ModInfo.PLUGIN_GUID); + Logger = new BAULogger(_manualLogSource); + var customLogListener = new CustomLogListener(Logger); BepInEx.Logging.Logger.Listeners.Add(customLogListener); ConsoleManager.SetConsoleColor(ConsoleColor.Green); ConsoleManager.ConsoleStream.WriteLine($".--------------------------------------------------------------------------------.\r\n| ____ _ _ _ _ _ |\r\n| | __ ) ___| |_| |_ ___ _ __ / \\ _ __ ___ ___ _ __ __ _ | | | |___ |\r\n| | _ \\ / _ \\ __| __/ _ \\ '__| / _ \\ | '_ ` _ \\ / _ \\| '_ \\ / _` | | | | / __||\r\n| | |_) | __/ |_| || __/ | / ___ \\| | | | | | (_) | | | | (_| | | |_| \\__ \\|\r\n| |____/ \\___|\\__|\\__\\___|_| /_/ \\_\\_| |_| |_|\\___/|_| |_|\\__, | \\___/|___/|\r\n| |___/ |\r\n'--------------------------------------------------------------------------------'"); diff --git a/src/Commands/DumpCommand.cs b/src/Commands/DumpCommand.cs index f34c933..d2bd7ad 100644 --- a/src/Commands/DumpCommand.cs +++ b/src/Commands/DumpCommand.cs @@ -22,56 +22,38 @@ internal sealed class DumpCommand : BaseCommand return base.CanRunCommand(out reason); } - private static string DecryptLog(string log) - { - string newLog = string.Empty; - string[] logArray = log.Split([Environment.NewLine], StringSplitOptions.None); - foreach (string text in logArray) - { - if (text.Contains("[PrivateLog]")) - { - newLog += text.Split(':')[0] + ":" + text.Split(':')[1].Replace("[PrivateLog]", "") + ": " + Encryptor.Decrypt(text.Split(':')[2][1..]) + "\n"; - } - else - { - newLog += text + "\n"; - } - } - return newLog; - } - internal override void Run() { - string logFilePath = BetterDataManager.Files.logFilePath; - if (!File.Exists(logFilePath)) + string bepInExLog = Path.Combine(Paths.BepInExRootPath, "LogOutput.log"); + if (!File.Exists(bepInExLog)) { - CommandErrorText("Log file not found!"); + CommandErrorText("BepInEx log file not found!"); return; } if (!BAUPlugin.ModInfo.Starlight) { - string log = File.ReadAllText(logFilePath); - string newLog = DecryptLog(log); - string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); - string logFolderPath = Path.Combine(desktopPath, "BetterLogDumps"); + string logFolderPath = Path.Combine(desktopPath, "BAULogDumps"); + if (!Directory.Exists(logFolderPath)) { Directory.CreateDirectory(logFolderPath); } - string logFileName = "log-" + BAUPlugin.ModInfo.VERSION_STRING + "-" + DateTime.Now.ToString("yyyy.MM.dd-HH.mm.ss") + "-bau" + ".log"; - string logFile = Path.Combine(logFolderPath, logFileName); - File.WriteAllText(logFile, newLog); - string bepInExLog = Path.Combine(Paths.BepInExRootPath, "LogOutput.log"); - if (File.Exists(bepInExLog)) + string log; + using (FileStream fileStream = new(bepInExLog, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + using (StreamReader reader = new(fileStream)) { - string logFileBepInExeName = "log-" + BAUPlugin.ModInfo.VERSION_STRING + "-" + DateTime.Now.ToString("yyyy.MM.dd-HH.mm.ss") + "-bepinex" + ".log"; - string logFileBepInExe = Path.Combine(logFolderPath, logFileBepInExeName); - File.Copy(bepInExLog, logFileBepInExe); + log = reader.ReadToEnd(); } + string decryptedLog = BAULogger.DecryptLogs(log); + + string logFileName = "log-" + BAUPlugin.ModInfo.VERSION_STRING + "-" + DateTime.Now.ToString("yyyy.MM.dd-HH.mm.ss") + "-bepinex" + ".log"; + string logFilePath_New = Path.Combine(logFolderPath, logFileName); + File.WriteAllText(logFilePath_New, decryptedLog); + System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo() { FileName = logFolderPath, @@ -84,27 +66,25 @@ internal sealed class DumpCommand : BaseCommand else { string dataPath = BetterDataManager.Folders.fileFolderPath; - string logFolderPath = Path.Combine(dataPath, "BetterLogDumps"); + string logFolderPath = Path.Combine(dataPath, "BAULogDumps"); if (!Directory.Exists(logFolderPath)) { Directory.CreateDirectory(logFolderPath); } - string androidLogContent = File.ReadAllText(logFilePath); - string androidNewLog = DecryptLog(androidLogContent); - - string androidLogFileName = "log-" + BAUPlugin.ModInfo.VERSION_STRING + "-" + DateTime.Now.ToString("yyyy.MM.dd-HH.mm.ss") + "-bau" + ".log"; - string androidNewLogFilePath = Path.Combine(logFolderPath, androidLogFileName); - File.WriteAllText(androidNewLogFilePath, androidNewLog); - - string bepInExLog = Path.Combine(Paths.BepInExRootPath, "LogOutput.log"); - if (File.Exists(bepInExLog)) + string log; + using (FileStream fileStream = new(bepInExLog, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + using (StreamReader reader = new(fileStream)) { - string logFileBepInExeName = "log-" + BAUPlugin.ModInfo.VERSION_STRING + "-" + DateTime.Now.ToString("yyyy.MM.dd-HH.mm.ss") + "-bepinex" + ".log"; - string logFileBepInExe = Path.Combine(logFolderPath, logFileBepInExeName); - File.Copy(bepInExLog, logFileBepInExe); + log = reader.ReadToEnd(); } + string decryptedLog = BAULogger.DecryptLogs(log); + + string logFileName = "log-" + BAUPlugin.ModInfo.VERSION_STRING + "-" + DateTime.Now.ToString("yyyy.MM.dd-HH.mm.ss") + "-bepinex" + ".log"; + string logFilePath_New = Path.Combine(logFolderPath, logFileName); + File.WriteAllText(logFilePath_New, decryptedLog); + CommandResultText($"Dump logs at '{logFolderPath}'"); } } diff --git a/src/Data/BetterDataManager.cs b/src/Data/BetterDataManager.cs index c31bb7b..702f0aa 100644 --- a/src/Data/BetterDataManager.cs +++ b/src/Data/BetterDataManager.cs @@ -65,12 +65,12 @@ internal static class BetterDataManager /// /// The log file path. /// - internal static readonly string logFilePath = Path.Combine(Folders.fileFolderPath, "better-log.txt"); + internal static readonly string logFilePath_Legacy = Path.Combine(Folders.fileFolderPath, "better-log.txt"); /// /// The previous log file path. /// - internal static readonly string previousLogFilePath = Path.Combine(Folders.fileFolderPath, "better-previous-log.txt"); + internal static readonly string previousLogFilePath_Legacy = Path.Combine(Folders.fileFolderPath, "better-previous-log.txt"); /// /// Legacy data file path (BetterData.json). @@ -204,6 +204,16 @@ internal static class BetterDataManager /// private static void LoadLegacyData() { + if (File.Exists(Files.logFilePath_Legacy)) + { + File.Delete(Files.logFilePath_Legacy); + } + + if (File.Exists(Files.previousLogFilePath_Legacy)) + { + File.Delete(Files.previousLogFilePath_Legacy); + } + if (File.Exists(Files.settingsFilePath_Legacy)) { if (File.Exists(Files.SettingsFilePath)) diff --git a/src/Data/Json/AbstractJsonFile.cs b/src/Data/Json/AbstractJsonFile.cs index f14bcb0..fd40e2b 100644 --- a/src/Data/Json/AbstractJsonFile.cs +++ b/src/Data/Json/AbstractJsonFile.cs @@ -80,7 +80,7 @@ internal abstract class AbstractJsonFile } catch (Exception ex) { - Logger_.Error(ex); + BAUPlugin.Logger.Error(ex); return false; } } @@ -101,7 +101,7 @@ internal abstract class AbstractJsonFile } catch (Exception ex) { - Logger_.Error(ex); + BAUPlugin.Logger.Error(ex); return false; } } diff --git a/src/Data/Json/BetterGameSettingsFile.cs b/src/Data/Json/BetterGameSettingsFile.cs index 856a833..96a9491 100644 --- a/src/Data/Json/BetterGameSettingsFile.cs +++ b/src/Data/Json/BetterGameSettingsFile.cs @@ -62,7 +62,7 @@ internal sealed class BetterGameSettingsFile : AbstractJsonFile } catch (Exception ex) { - Logger_.Error($"Failed to convert JSON element for key {kvp.Key}: {ex.Message}"); + BAUPlugin.Logger.Error($"Failed to convert JSON element for key {kvp.Key}: {ex.Message}"); } } } diff --git a/src/Data/SharedSecretExchange.cs b/src/Data/SharedSecretExchange.cs index a5099bc..cff3e55 100644 --- a/src/Data/SharedSecretExchange.cs +++ b/src/Data/SharedSecretExchange.cs @@ -39,7 +39,7 @@ internal sealed class SharedSecretExchange } catch (Exception ex) { - Logger_.Error("ECDH unavailable, using fallback handshake: " + ex.Message, logConsole: false); + BAUPlugin.Logger.Error("ECDH unavailable, using fallback handshake: " + ex.Message, logConsole: false); cryptoDisabled = true; dh = null; publicKey = []; @@ -129,7 +129,7 @@ internal sealed class SharedSecretExchange } catch (Exception ex) { - Logger_.Error("Error generating shared secret: " + ex.Message, logConsole: false); + BAUPlugin.Logger.Error("Error generating shared secret: " + ex.Message, logConsole: false); return []; } } diff --git a/src/Managers/BetterNotificationManager.cs b/src/Managers/BetterNotificationManager.cs index 33b7f3a..212ea0d 100644 --- a/src/Managers/BetterNotificationManager.cs +++ b/src/Managers/BetterNotificationManager.cs @@ -172,8 +172,8 @@ internal static class BetterNotificationManager Notify(text, time: 8f); } - Logger_.LogCheat($"{player.cosmetics.nameText.text} Info: {player.Data.PlayerName} - {player.Data.FriendCode} - {player.GetHashPuid()}"); - Logger_.LogCheat(Utils.RemoveHtmlText(rawText)); + BAUPlugin.Logger.LogCheat($"{player.cosmetics.nameText.text} Info: {player.Data.PlayerName} - {player.Data.FriendCode} - {player.GetHashPuid()}"); + BAUPlugin.Logger.LogCheat(Utils.RemoveHtmlText(rawText)); if (GameState.IsHost && kickPlayer) { diff --git a/src/Managers/NetworkManager.cs b/src/Managers/NetworkManager.cs index 3a9e9e9..e2dcc0c 100644 --- a/src/Managers/NetworkManager.cs +++ b/src/Managers/NetworkManager.cs @@ -41,7 +41,7 @@ internal static class NetworkManager } catch (Exception ex) { - Logger_.Error(ex, "NetworkManager"); + BAUPlugin.Logger.Error(ex, "NetworkManager"); } finally { @@ -414,7 +414,7 @@ internal static class NetworkManager /// private static void HandleInvalidTag(MessageReader reader) { - Logger_.Warning($"Bad tag {reader.Tag} at {reader.Offset}+{reader.Position}={reader.Length}: " + string.Join(" ", reader.Buffer.Take(128)), "NetworkManager"); + BAUPlugin.Logger.Warning($"Bad tag {reader.Tag} at {reader.Offset}+{reader.Position}={reader.Length}: " + string.Join(" ", reader.Buffer.Take(128)), "NetworkManager"); reader.Recycle(); } @@ -477,7 +477,7 @@ internal static class NetworkManager if (BetterAntiCheat.CheckCancelRPC(player, callId, reader) == false) { - if (!player.IsLocalPlayer()) Logger_.LogCheat($"RPC canceled by Anti-Cheat: {Enum.GetName((RpcCalls)callId)}{Enum.GetName((CustomRPC)callId)} - {callId}"); + if (!player.IsLocalPlayer()) BAUPlugin.Logger.LogCheat($"RPC canceled by Anti-Cheat: {Enum.GetName((RpcCalls)callId)}{Enum.GetName((CustomRPC)callId)} - {callId}"); return false; } diff --git a/src/Modules/AntiCheat/BetterAntiCheat.cs b/src/Modules/AntiCheat/BetterAntiCheat.cs index 374619e..09cf7bf 100644 --- a/src/Modules/AntiCheat/BetterAntiCheat.cs +++ b/src/Modules/AntiCheat/BetterAntiCheat.cs @@ -164,7 +164,7 @@ internal static class BetterAntiCheat { if (BetterNotificationManager.NotifyCheat(player, TranslationStrings.AntiCheat_InvalidHostRPC.Format(Enum.GetName((RpcCalls)callId)))) { - Logger_.LogCheat($"{player.ExtendedData().RealName} {Enum.GetName((RpcCalls)callId)}: {!player.IsHost()}"); + BAUPlugin.Logger.LogCheat($"{player.ExtendedData().RealName} {Enum.GetName((RpcCalls)callId)}: {!player.IsHost()}"); } reader.Recycle(); @@ -183,7 +183,7 @@ internal static class BetterAntiCheat { if (BetterNotificationManager.NotifyCheat(player, TranslationStrings.AntiCheat_InvalidSetRPC.Format(Enum.GetName((RpcCalls)callId)))) { - Logger_.LogCheat($"{player.ExtendedData().RealName} {Enum.GetName((RpcCalls)callId)}: {GameState.IsInGamePlay}"); + BAUPlugin.Logger.LogCheat($"{player.ExtendedData().RealName} {Enum.GetName((RpcCalls)callId)}: {GameState.IsInGamePlay}"); } reader.Recycle(); @@ -223,7 +223,7 @@ internal static class BetterAntiCheat { if (BetterNotificationManager.NotifyCheat(player, TranslationStrings.AntiCheat_InvalidLobbyRPC.Format(Enum.GetName((RpcCalls)callId)))) { - Logger_.LogCheat($"{player.ExtendedData().RealName} {Enum.GetName((RpcCalls)callId)}: {GameState.IsInGame} && {GameState.IsLobby}"); + BAUPlugin.Logger.LogCheat($"{player.ExtendedData().RealName} {Enum.GetName((RpcCalls)callId)}: {GameState.IsInGame} && {GameState.IsLobby}"); } reader.Recycle(); @@ -237,7 +237,7 @@ internal static class BetterAntiCheat } catch (Exception ex) { - Logger_.Error(ex); + BAUPlugin.Logger.Error(ex); return true; } } diff --git a/src/Modules/AntiCheat/RPCHandler.cs b/src/Modules/AntiCheat/RPCHandler.cs index 5fe8644..f62dc01 100644 --- a/src/Modules/AntiCheat/RPCHandler.cs +++ b/src/Modules/AntiCheat/RPCHandler.cs @@ -164,7 +164,7 @@ internal abstract class RPCHandler } catch (Exception ex) { - Logger_.Error(ex); + BAUPlugin.Logger.Error(ex); } } } @@ -181,7 +181,7 @@ internal abstract class RPCHandler { string Name = Enum.GetName((RpcCalls)CallId) ?? Enum.GetName((CustomRPC)CallId) ?? $"Unregistered({CallId})"; Name = $"[{Enum.GetName(catchedHandlerFlag)}] > " + Name; - Logger_.LogCheat($"{catchedSender?.ExtendedData()?.RealName ?? player.ExtendedData()?.RealName ?? string.Empty} {Name}: {info}"); + BAUPlugin.Logger.LogCheat($"{catchedSender?.ExtendedData()?.RealName ?? player.ExtendedData()?.RealName ?? string.Empty} {Name}: {info}"); } /// diff --git a/src/Modules/AntiCheat/RPCHandlers/Cheats/AUMChatHandler.cs b/src/Modules/AntiCheat/RPCHandlers/Cheats/AUMChatHandler.cs index d1c9125..ca09c2f 100644 --- a/src/Modules/AntiCheat/RPCHandlers/Cheats/AUMChatHandler.cs +++ b/src/Modules/AntiCheat/RPCHandlers/Cheats/AUMChatHandler.cs @@ -34,7 +34,7 @@ internal sealed class AUMChatHandler : RPCHandler betterData.AntiCheatInfo.AUMChats.Add(msgString); } - Logger_.Log($"{sender.Data.PlayerName} -> {msgString}", "AUMChatLog"); + BAUPlugin.Logger.Log($"{sender.Data.PlayerName} -> {msgString}", "AUMChatLog"); if (BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_Anticheat)) return; diff --git a/src/Modules/AntiCheat/RPCHandlers/CheckNameHandler.cs b/src/Modules/AntiCheat/RPCHandlers/CheckNameHandler.cs index d6d631b..7dc8206 100644 --- a/src/Modules/AntiCheat/RPCHandlers/CheckNameHandler.cs +++ b/src/Modules/AntiCheat/RPCHandlers/CheckNameHandler.cs @@ -25,7 +25,7 @@ internal sealed class CheckNameHandler : RPCHandler if (BetterNotificationManager.NotifyCheat(sender, GetFormatSetText())) { Utils.AddChatPrivate($"{sender.GetPlayerNameAndColor()} Has tried to change their name to '{name}' but has been undone!"); - Logger_.LogCheat($"{sender.ExtendedData().RealName} Has tried to change their name to '{name}' but has been undone!"); + BAUPlugin.Logger.LogCheat($"{sender.ExtendedData().RealName} Has tried to change their name to '{name}' but has been undone!"); LogRpcInfo($"{sender.DataIsCollected() == true} && {!GameState.IsLocalGame} && {GameState.IsVanillaServer}"); } diff --git a/src/Modules/AntiCheat/RPCHandlers/SetNameHandler.cs b/src/Modules/AntiCheat/RPCHandlers/SetNameHandler.cs index fed8924..d32d295 100644 --- a/src/Modules/AntiCheat/RPCHandlers/SetNameHandler.cs +++ b/src/Modules/AntiCheat/RPCHandlers/SetNameHandler.cs @@ -23,7 +23,7 @@ internal sealed class SetNameHandler : RPCHandler if (BetterNotificationManager.NotifyCheat(sender, GetFormatSetText())) { Utils.AddChatPrivate($"{sender.GetPlayerNameAndColor()} Has tried to change their name to '{name}' but has been undone!"); - Logger_.LogCheat($"{sender.ExtendedData().RealName} Has tried to change their name to '{name}' but has been undone!"); + BAUPlugin.Logger.LogCheat($"{sender.ExtendedData().RealName} Has tried to change their name to '{name}' but has been undone!"); LogRpcInfo($"Player attempted to change name multiple times: '{name}'"); } diff --git a/src/Modules/BAULogger.cs b/src/Modules/BAULogger.cs new file mode 100644 index 0000000..3b180ae --- /dev/null +++ b/src/Modules/BAULogger.cs @@ -0,0 +1,214 @@ +using BepInEx; +using BepInEx.Logging; +using BetterAmongUs.Utilities; + +namespace BetterAmongUs.Modules; + +/// +/// Provides logging for BetterAmongUs with various log levels and destinations. +/// +internal sealed class BAULogger(ManualLogSource manualLogSource) +{ + private readonly ManualLogSource _manualLogSource = manualLogSource; + + /// + /// Logs a message with specified parameters. + /// + /// The message to log. + /// The log tag/category. + /// Whether to output to console. + /// The console color for the message. + /// Whether to log only when the client is host. + internal void Log(string info, string tag = "Log", bool logConsole = true, ConsoleColor color = ConsoleColor.White, bool hostOnly = false) + { + try + { + if (hostOnly && !GameState.IsHost) + return; + + _manualLogSource.LogInfo($"[{tag}] {info}"); + if (logConsole) + { + ConsoleManager.SetConsoleColor(color); + ConsoleManager.ConsoleStream.WriteLine($"{DateTime.Now:HH:mm} BetterAmongUs[{tag}]: {Utils.RemoveHtmlText(info)}"); + } + } + catch { } + } + + /// + /// Logs a header message with visual formatting. + /// + /// The header text. + /// The log tag/category. + /// Whether to log only when the client is host. + /// Whether to output to console. + internal void LogHeader(string info, string tag = "LogHeader", bool hostOnly = false, bool logConsole = true) => Log($" >-------------- {info} --------------<", tag, hostOnly: hostOnly, logConsole: logConsole); + + /// + /// Logs cheat detection messages with green console color. + /// + /// The cheat detection message. + /// The log tag/category. + /// Whether to log only when the client is host. + /// Whether to output to console. + internal void LogCheat(string info, string tag = "AntiCheat", bool hostOnly = false, bool logConsole = true) => Log(info, tag, color: ConsoleColor.Green, hostOnly: hostOnly, logConsole: logConsole); + + /// + /// Logs error messages with red console color. + /// + /// The error message. + /// The log tag/category. + /// Whether to log only when the client is host. + /// Whether to output to console. + internal void Error(string info, string tag = "Error", bool hostOnly = false, bool logConsole = true) => Log(info, tag, color: ConsoleColor.Red, hostOnly: hostOnly, logConsole: logConsole); + + /// + /// Logs exception details with red console color. + /// + /// The exception to log. + /// The log tag/category. + /// Whether to log only when the client is host. + /// Whether to output to console. + internal void Error(Exception ex, string tag = "Error", bool hostOnly = false, bool logConsole = true) => Log(ex.ToString(), tag, color: ConsoleColor.Red, hostOnly: hostOnly, logConsole: logConsole); + + /// + /// Logs warning messages with yellow console color. + /// + /// The warning message. + /// The log tag/category. + /// Whether to log only when the client is host. + /// Whether to output to console. + internal void Warning(string info, string tag = "Warning", bool hostOnly = false, bool logConsole = true) => Log(info, tag, color: ConsoleColor.Yellow, hostOnly: hostOnly, logConsole: logConsole); + + /// + /// Logs a test message for debugging purposes. + /// + internal void Test() + { + Log("------------------> TEST <------------------", "TEST"); + InGame("TEST"); + } + + /// + /// Logs a message in-game via the disconnect message notifier. + /// + /// The message to display in-game. + /// Whether to log only when the client is host. + internal void InGame(string info, bool hostOnly = false) + { + if (hostOnly && !GameState.IsHost) + return; + + if (HudManager.InstanceExists) HudManager.Instance.Notifier.AddDisconnectMessage(info); + Log(info, "InGame", hostOnly: hostOnly); + } + + /// + /// Logs a private message with encryption for sensitive data. + /// + /// The sensitive message to log. + /// The log tag/category. + /// Whether to log only when the client is host. + internal void LogPrivate(string info, string tag = "Log", bool hostOnly = false) + { + try + { + if (hostOnly && !GameState.IsHost) + return; + + string encrypted = Encryptor.Encrypt(info); + Log(BAUPlugin.Constants.ENCRYPTED_LOG_PREFIX + encrypted + BAUPlugin.Constants.ENCRYPTED_LOG_POSTFIX, tag, false); + } + catch { } + } + + /// + /// Decrypts all encrypted sections within a log file text. + /// + /// The raw log file content containing encrypted sections marked with prefix and postfix characters. + /// The log text with all encrypted sections decrypted and markers removed. + internal static string DecryptLogs(string logText) + { + string newLog = string.Empty; + string[] logArray = logText.Split([Environment.NewLine], StringSplitOptions.None); + + foreach (string text in logArray) + { + if (text.Contains(BAUPlugin.Constants.ENCRYPTED_LOG_PREFIX) && + text.Contains(BAUPlugin.Constants.ENCRYPTED_LOG_POSTFIX)) + { + int start = text.IndexOf(BAUPlugin.Constants.ENCRYPTED_LOG_PREFIX); + int end = text.IndexOf(BAUPlugin.Constants.ENCRYPTED_LOG_POSTFIX); + + string before = text[..start]; + string encrypted = text.Substring(start + 1, end - start - 1); + string after = text[(end + 1)..]; + + try + { + string decrypted = Encryptor.Decrypt(encrypted); + newLog += before + decrypted + after + "\n"; + } + catch + { + newLog += text + "\n"; + } + } + else + { + newLog += text + "\n"; + } + } + + return newLog; + } +} + +/// +/// Custom log listener for BepInEx that forwards logs to the BetterAmongUs logging system. +/// +internal class CustomLogListener(BAULogger bauLogger) : ILogListener +{ + private readonly BAULogger _bauLogger = bauLogger; + + /// + /// Gets or sets the log levels to filter. + /// + public LogLevel LogLevelFilter { get; set; } = LogLevel.Info | LogLevel.Warning | LogLevel.Error; + + /// + /// Handles log events from BepInEx. + /// + /// The event sender. + /// The log event arguments. + public void LogEvent(object sender, LogEventArgs eventArgs) + { + if (eventArgs.Source.SourceName.ToLower().Contains("unity") + || eventArgs.Source.SourceName.ToLower().Contains("betteramongus")) + return; + + if (eventArgs.Level is LogLevel.None or LogLevel.Info) + { + _bauLogger.Log(eventArgs.Data.ToString(), "BepInEx." + eventArgs.Source.SourceName, logConsole: false); + } + else if (eventArgs.Level is LogLevel.Warning) + { + _bauLogger.Warning(eventArgs.Data.ToString(), "BepInEx." + eventArgs.Source.SourceName, logConsole: false); + } + else if (eventArgs.Level is LogLevel.Error or LogLevel.Fatal) + { + _bauLogger.Error(eventArgs.Data.ToString(), "BepInEx." + eventArgs.Source.SourceName, logConsole: false); + } + } + + /// + /// Disposes the log listener. + /// + public void Dispose() { } + + /// + /// Flushes the log listener. + /// + public void Flush() { } +} \ No newline at end of file diff --git a/src/Modules/Encryptor.cs b/src/Modules/Encryptor.cs index ea241e9..94d4844 100644 --- a/src/Modules/Encryptor.cs +++ b/src/Modules/Encryptor.cs @@ -1,5 +1,5 @@ using System.Security.Cryptography; -using System.Text; +using UnityEngine; namespace BetterAmongUs.Modules; @@ -8,16 +8,82 @@ namespace BetterAmongUs.Modules; /// internal static class Encryptor { - private static readonly byte[] Key = Encoding.UTF8.GetBytes("0123456789ABCDEF0123456789ABCDEF"); - private static readonly byte[] IV = Encoding.UTF8.GetBytes("ABCDEF0123456789"); + private static byte[] Key = []; + private static byte[] IV = []; + + /// + /// Initializes the encryption system by loading or generating AES keys. + /// + internal static void Initialize() + { + try + { + if (PlayerPrefs.HasKey("BAUEncryptionKey") && PlayerPrefs.HasKey("BAUEncryptionIV")) + { + string keyString = PlayerPrefs.GetString("BAUEncryptionKey"); + string ivString = PlayerPrefs.GetString("BAUEncryptionIV"); + + if (string.IsNullOrEmpty(keyString) || string.IsNullOrEmpty(ivString)) + { + throw new Exception("Keys are empty"); + } + + Key = Convert.FromBase64String(keyString); + IV = Convert.FromBase64String(ivString); + + if (Key.Length != 32 || IV.Length != 16) + { + throw new Exception("Invalid key length"); + } + } + else + { + GenerateAndSaveKeys(); + } + } + catch + { + PlayerPrefs.DeleteKey("BAUEncryptionKey"); + PlayerPrefs.DeleteKey("BAUEncryptionIV"); + PlayerPrefs.Save(); + GenerateAndSaveKeys(); + } + } + + /// + /// Generates a new 256-bit AES key and 128-bit initialization vector (IV), + /// then saves them to PlayerPrefs as Base64 strings. + /// + private static void GenerateAndSaveKeys() + { + using (Aes aes = Aes.Create()) + { + aes.KeySize = 256; + aes.BlockSize = 128; + aes.GenerateKey(); + aes.GenerateIV(); + Key = aes.Key; + IV = aes.IV; + } + + PlayerPrefs.SetString("BAUEncryptionKey", Convert.ToBase64String(Key)); + PlayerPrefs.SetString("BAUEncryptionIV", Convert.ToBase64String(IV)); + PlayerPrefs.Save(); + } /// /// Encrypts a plain text string using AES encryption. /// /// The plain text string to encrypt. /// A base64-encoded string containing the encrypted data. + /// Thrown if encryption is not initialized. internal static string Encrypt(string input) { + if (Key == null || Key.Length == 0 || IV == null || IV.Length == 0) + { + throw new InvalidOperationException("Encryption not initialized. Call InitializeEncryption() first."); + } + using Aes aes = Aes.Create(); aes.Key = Key; aes.IV = IV; @@ -36,8 +102,16 @@ internal static class Encryptor /// /// The base64-encoded encrypted string. /// The decrypted plain text string. + /// Thrown if encryption is not initialized. + /// Thrown if the input is not valid Base64. + /// Thrown if decryption fails due to invalid key or corrupted data. internal static string Decrypt(string input) { + if (Key == null || Key.Length == 0 || IV == null || IV.Length == 0) + { + throw new InvalidOperationException("Encryption not initialized. Call InitializeEncryption() first."); + } + using Aes aes = Aes.Create(); aes.Key = Key; aes.IV = IV; diff --git a/src/Modules/Logger.cs b/src/Modules/Logger.cs deleted file mode 100644 index 5d3c722..0000000 --- a/src/Modules/Logger.cs +++ /dev/null @@ -1,240 +0,0 @@ -using BepInEx; -using BepInEx.Logging; -using BetterAmongUs.Data; -using BetterAmongUs.Utilities; -using System.Diagnostics; -using System.Runtime.CompilerServices; - -namespace BetterAmongUs.Modules; - -/// -/// Provides logging utilities for BetterAmongUs with various log levels and destinations. -/// -internal static class Logger_ -{ - /// - /// Logs a message with specified parameters. - /// - /// The message to log. - /// The log tag/category. - /// Whether to output to console. - /// The console color for the message. - /// Whether to log only when the client is host. - internal static void Log(string info, string tag = "Log", bool logConsole = true, ConsoleColor color = ConsoleColor.White, bool hostOnly = false) - { - try - { - if (hostOnly && !GameState.IsHost) - return; - - string mark = $"{DateTime.Now:HH:mm} [BetterLog][{tag}]"; - string logFilePath = BetterDataManager.Files.logFilePath; - string newLine = $"{mark}: {Utils.RemoveHtmlText(info)}"; - File.AppendAllText(logFilePath, newLine + Environment.NewLine); - BAUPlugin.Logger.LogInfo($"[{tag}] {info}"); - if (logConsole) - { - ConsoleManager.SetConsoleColor(color); - ConsoleManager.ConsoleStream.WriteLine($"{DateTime.Now:HH:mm} BetterAmongUs[{tag}]: {Utils.RemoveHtmlText(info)}"); - } - } - catch { } - } - - /// - /// Logs method calls with detailed information about the caller. - /// - /// Additional information about the method call. - /// The type containing the method (optional). - /// Whether to log only when the client is host. - /// Automatically populated: path of the file containing the caller. - /// Automatically populated: line number of the caller. - /// Automatically populated: name of the calling member. - internal static void LogMethod( - string info = "", - Type? runtimeType = null, - bool hostOnly = false, - [CallerFilePath] string callerFilePath = "", - [CallerLineNumber] int callerLineNumber = 0, - [CallerMemberName] string callerMemberName = "") - { - var loggedMethodFrame = new StackFrame(1, true); - - var loggedMethod = loggedMethodFrame.GetMethod(); - string loggedMethodName = loggedMethod.Name; - string? loggedClassFullName = runtimeType?.FullName ?? loggedMethod.DeclaringType?.FullName; - string? loggedClassName = runtimeType?.Name ?? loggedMethod.DeclaringType?.Name; - - string logMessage = string.IsNullOrEmpty(info) - ? $"{loggedClassFullName}.{loggedMethodName} was called from {Path.GetFileName(callerFilePath)}({callerLineNumber}) in {callerMemberName}." - : $"{loggedClassFullName}.{loggedMethodName} was called from {Path.GetFileName(callerFilePath)}({callerLineNumber}) in {callerMemberName}. Info: {info}."; - - Log(logMessage, "MethodLog", hostOnly); - } - - /// - /// Logs private method calls with detailed information, using encrypted storage. - /// - /// Additional information about the method call. - /// The type containing the method (optional). - /// Whether to log only when the client is host. - /// Automatically populated: path of the file containing the caller. - /// Automatically populated: line number of the caller. - /// Automatically populated: name of the calling member. - internal static void LogMethodPrivate( - string info = "", - Type? runtimeType = null, - bool hostOnly = false, - [CallerFilePath] string callerFilePath = "", - [CallerLineNumber] int callerLineNumber = 0, - [CallerMemberName] string callerMemberName = "") - { - var loggedMethodFrame = new StackFrame(1, true); - - var loggedMethod = loggedMethodFrame.GetMethod(); - string loggedMethodName = loggedMethod.Name; - string? loggedClassFullName = runtimeType?.FullName ?? loggedMethod.DeclaringType?.FullName; - string? loggedClassName = runtimeType?.Name ?? loggedMethod.DeclaringType?.Name; - - string logMessage = string.IsNullOrEmpty(info) - ? $"{loggedClassFullName}.{loggedMethodName} was called from {Path.GetFileName(callerFilePath)}({callerLineNumber}) in {callerMemberName}." - : $"{loggedClassFullName}.{loggedMethodName} was called from {Path.GetFileName(callerFilePath)}({callerLineNumber}) in {callerMemberName}. Info: {info}."; - - LogPrivate(logMessage, "MethodLog", hostOnly); - } - - /// - /// Logs a header message with visual formatting. - /// - /// The header text. - /// The log tag/category. - /// Whether to log only when the client is host. - /// Whether to output to console. - internal static void LogHeader(string info, string tag = "LogHeader", bool hostOnly = false, bool logConsole = true) => Log($" >-------------- {info} --------------<", tag, hostOnly: hostOnly, logConsole: logConsole); - - /// - /// Logs cheat detection messages with green console color. - /// - /// The cheat detection message. - /// The log tag/category. - /// Whether to log only when the client is host. - /// Whether to output to console. - internal static void LogCheat(string info, string tag = "AntiCheat", bool hostOnly = false, bool logConsole = true) => Log(info, tag, color: ConsoleColor.Green, hostOnly: hostOnly, logConsole: logConsole); - - /// - /// Logs error messages with red console color. - /// - /// The error message. - /// The log tag/category. - /// Whether to log only when the client is host. - /// Whether to output to console. - internal static void Error(string info, string tag = "Error", bool hostOnly = false, bool logConsole = true) => Log(info, tag, color: ConsoleColor.Red, hostOnly: hostOnly, logConsole: logConsole); - - /// - /// Logs exception details with red console color. - /// - /// The exception to log. - /// The log tag/category. - /// Whether to log only when the client is host. - /// Whether to output to console. - internal static void Error(Exception ex, string tag = "Error", bool hostOnly = false, bool logConsole = true) => Log(ex.ToString(), tag, color: ConsoleColor.Red, hostOnly: hostOnly, logConsole: logConsole); - - /// - /// Logs warning messages with yellow console color. - /// - /// The warning message. - /// The log tag/category. - /// Whether to log only when the client is host. - /// Whether to output to console. - internal static void Warning(string info, string tag = "Warning", bool hostOnly = false, bool logConsole = true) => Log(info, tag, color: ConsoleColor.Yellow, hostOnly: hostOnly, logConsole: logConsole); - - /// - /// Logs a test message for debugging purposes. - /// - internal static void Test() - { - Log("------------------> TEST <------------------", "TEST"); - InGame("TEST"); - } - - /// - /// Logs a message in-game via the disconnect message notifier. - /// - /// The message to display in-game. - /// Whether to log only when the client is host. - internal static void InGame(string info, bool hostOnly = false) - { - if (hostOnly && !GameState.IsHost) - return; - - if (HudManager.InstanceExists) HudManager.Instance.Notifier.AddDisconnectMessage(info); - Log(info, "InGame", hostOnly: hostOnly); - } - - /// - /// Logs a private message with encryption for sensitive data. - /// - /// The sensitive message to log. - /// The log tag/category. - /// Whether to log only when the client is host. - internal static void LogPrivate(string info, string tag = "Log", bool hostOnly = false) - { - try - { - if (hostOnly && !GameState.IsHost) - return; - - string mark = $"{DateTime.Now:HH:mm} [BetterLog][PrivateLog][{tag}]"; - string logFilePath = BetterDataManager.Files.logFilePath; - string newLine = $"{mark}: " + Encryptor.Encrypt($"{info}"); - File.AppendAllText(logFilePath, newLine + Environment.NewLine); - } - catch { } - } -} - -/// -/// Custom log listener for BepInEx that forwards logs to the BetterAmongUs logging system. -/// -internal class CustomLogListener : ILogListener -{ - /// - /// Gets or sets the log levels to filter. - /// - public LogLevel LogLevelFilter { get; set; } = LogLevel.Info | LogLevel.Warning | LogLevel.Error; - - /// - /// Handles log events from BepInEx. - /// - /// The event sender. - /// The log event arguments. - public void LogEvent(object sender, LogEventArgs eventArgs) - { - if (eventArgs.Source.SourceName.ToLower().Contains("unity") - || eventArgs.Source.SourceName.ToLower().Contains("betteramongus")) - return; - - if (eventArgs.Level is LogLevel.None or LogLevel.Info) - { - Logger_.Log(eventArgs.Data.ToString(), "BepInEx." + eventArgs.Source.SourceName, logConsole: false); - } - else if (eventArgs.Level is LogLevel.Warning) - { - Logger_.Warning(eventArgs.Data.ToString(), "BepInEx." + eventArgs.Source.SourceName, logConsole: false); - } - else if (eventArgs.Level is LogLevel.Error or LogLevel.Fatal) - { - Logger_.Error(eventArgs.Data.ToString(), "BepInEx." + eventArgs.Source.SourceName, logConsole: false); - } - } - - /// - /// Disposes the log listener. - /// - public void Dispose() { } - - /// - /// Flushes the log listener. - /// - public void Flush() { } -} \ No newline at end of file diff --git a/src/Modules/Support/BAUSupportEvent.cs b/src/Modules/Support/BAUSupportEvent.cs index ab9b462..5a09816 100644 --- a/src/Modules/Support/BAUSupportEvent.cs +++ b/src/Modules/Support/BAUSupportEvent.cs @@ -80,7 +80,7 @@ internal abstract class BAUSupportEvent(string attributeFlag) } catch (Exception ex) { - Logger_.Error($"Invoking event '{_attributeFlag}' for {pluginInfo.GetType().Name}: {ex}"); + BAUPlugin.Logger.Error($"Invoking event '{_attributeFlag}' for {pluginInfo.GetType().Name}: {ex}"); return default; } } diff --git a/src/Modules/Translator.cs b/src/Modules/Translator.cs index 063706a..d69ef04 100644 --- a/src/Modules/Translator.cs +++ b/src/Modules/Translator.cs @@ -22,9 +22,9 @@ internal static class Translator /// internal static void Initialize() { - Logger_.Log("Loading language files...", "Translator"); + BAUPlugin.Logger.Log("Loading language files...", "Translator"); LoadLanguages(); - Logger_.Log("Language files loaded successfully", "Translator"); + BAUPlugin.Logger.Log("Language files loaded successfully", "Translator"); } /// @@ -41,7 +41,7 @@ internal static class Translator if (jsonFileNames.Length == 0) { - Logger_.Error("JSON translation files do not exist.", "Translator"); + BAUPlugin.Logger.Error("JSON translation files do not exist.", "Translator"); return; } @@ -54,7 +54,7 @@ internal static class Translator } catch (Exception ex) { - Logger_.Error($"Error loading languages: {ex}", "Translator"); + BAUPlugin.Logger.Error($"Error loading languages: {ex}", "Translator"); } } @@ -89,7 +89,7 @@ internal static class Translator if (jsonDictionary == null) { - Logger_.Error($"Failed to deserialize JSON from {resourceName}", "Translator"); + BAUPlugin.Logger.Error($"Failed to deserialize JSON from {resourceName}", "Translator"); return; } @@ -103,12 +103,12 @@ internal static class Translator } else { - Logger_.Error($"Invalid JSON format in {resourceName}: Missing or invalid 'LanguageID' field.", "Translator"); + BAUPlugin.Logger.Error($"Invalid JSON format in {resourceName}: Missing or invalid 'LanguageID' field.", "Translator"); } } catch (Exception ex) { - Logger_.Error($"Error loading language file {resourceName}: {ex}", "Translator"); + BAUPlugin.Logger.Error($"Error loading language file {resourceName}: {ex}", "Translator"); } } @@ -176,7 +176,7 @@ internal static class Translator } catch (Exception ex) { - Logger_.Error($"Error retrieving string [{translationString}]: {ex}", "Translator"); + BAUPlugin.Logger.Error($"Error retrieving string [{translationString}]: {ex}", "Translator"); return fallbackText; } } diff --git a/src/MonoScripts/BetterPingTracker.cs b/src/MonoScripts/BetterPingTracker.cs index aeace0b..2b8c23c 100644 --- a/src/MonoScripts/BetterPingTracker.cs +++ b/src/MonoScripts/BetterPingTracker.cs @@ -39,7 +39,7 @@ internal sealed class BetterPingTracker : MonoBehaviour if (pingText == null || pingAspectPosition == null) { - Logger_.Error("BetterPingTracker.SetUp() called with null parameters!"); + BAUPlugin.Logger.Error("BetterPingTracker.SetUp() called with null parameters!"); return; } diff --git a/src/MonoScripts/Extended/ExtendedPlayerInfo.cs b/src/MonoScripts/Extended/ExtendedPlayerInfo.cs index a9a55f3..5d33535 100644 --- a/src/MonoScripts/Extended/ExtendedPlayerInfo.cs +++ b/src/MonoScripts/Extended/ExtendedPlayerInfo.cs @@ -29,7 +29,7 @@ internal sealed class ExtendedPlayerInfo : MonoBehaviour, IMonoExtension(<#FFFFFF>{currentVotes}<#6F6F6F>/<#FFFFFF>{maxVotes}<#6F6F6F>)" diff --git a/src/Patches/Gameplay/LobbyPatch.cs b/src/Patches/Gameplay/LobbyPatch.cs index 5d44588..d9b352a 100644 --- a/src/Patches/Gameplay/LobbyPatch.cs +++ b/src/Patches/Gameplay/LobbyPatch.cs @@ -160,6 +160,6 @@ internal static class LobbyPatch [HarmonyPrefix] private static void GameStartManager_FinallyBegin_Prefix(/*GameStartManager __instance*/) { - Logger_.LogHeader($"Game Has Started - {Enum.GetName(typeof(MapNames), GameState.GetActiveMapId)}/{GameState.GetActiveMapId}", "GamePlayManager"); + BAUPlugin.Logger.LogHeader($"Game Has Started - {Enum.GetName(typeof(MapNames), GameState.GetActiveMapId)}/{GameState.GetActiveMapId}", "GamePlayManager"); } } \ No newline at end of file diff --git a/src/Patches/Gameplay/Player/PlayerControlPatch.cs b/src/Patches/Gameplay/Player/PlayerControlPatch.cs index 825489a..74fb2db 100644 --- a/src/Patches/Gameplay/Player/PlayerControlPatch.cs +++ b/src/Patches/Gameplay/Player/PlayerControlPatch.cs @@ -88,7 +88,7 @@ internal static class PlayerControlPatch return; // Log kill event with player names and roles - Logger_.LogPrivate($"{__instance.Data.PlayerName} Has killed {target.Data.PlayerName} as {__instance.Data.RoleType.GetRoleName()}", "EventLog"); + BAUPlugin.Logger.LogPrivate($"{__instance.Data.PlayerName} Has killed {target.Data.PlayerName} as {__instance.Data.RoleType.GetRoleName()}", "EventLog"); // Track kill count in player's BetterData if (resultFlags.HasFlag(MurderResultFlags.Succeeded) || resultFlags.HasFlag(MurderResultFlags.DecisionByHost)) @@ -118,9 +118,9 @@ internal static class PlayerControlPatch // Log shapeshift events (both shifting and unshifting) if (__instance != targetPlayer) - Logger_.LogPrivate($"{__instance.Data.PlayerName} Has Shapeshifted into {targetPlayer.Data.PlayerName}, did animate: {animate}", "EventLog"); + BAUPlugin.Logger.LogPrivate($"{__instance.Data.PlayerName} Has Shapeshifted into {targetPlayer.Data.PlayerName}, did animate: {animate}", "EventLog"); else - Logger_.LogPrivate($"{__instance.Data.PlayerName} Has Un-Shapeshifted, did animate: {animate}", "EventLog"); + BAUPlugin.Logger.LogPrivate($"{__instance.Data.PlayerName} Has Un-Shapeshifted, did animate: {animate}", "EventLog"); } private static IEnumerator CoSetColorEarly(PlayerControl __instance) @@ -135,9 +135,9 @@ internal static class PlayerControlPatch { // Log Phantom role visibility changes if (isActive) - Logger_.LogPrivate($"{__instance.Data.PlayerName} Has Vanished as Phantom, did animate: {shouldAnimate}", "EventLog"); + BAUPlugin.Logger.LogPrivate($"{__instance.Data.PlayerName} Has Vanished as Phantom, did animate: {shouldAnimate}", "EventLog"); else - Logger_.LogPrivate($"{__instance.Data.PlayerName} Has Appeared as Phantom, did animate: {shouldAnimate}", "EventLog"); + BAUPlugin.Logger.LogPrivate($"{__instance.Data.PlayerName} Has Appeared as Phantom, did animate: {shouldAnimate}", "EventLog"); } [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.SetName))] @@ -163,7 +163,7 @@ internal static class PlayerControlPatch private static void PlayerPhysics_BootFromVent_Postfix(PlayerPhysics __instance, int ventId) { // Log vent boot events (when engineer boots someone) - Logger_.LogPrivate($"{__instance.myPlayer.Data.PlayerName} Has been booted from vent: {ventId}, as {__instance.myPlayer.Data.RoleType.GetRoleName()}", "EventLog"); + BAUPlugin.Logger.LogPrivate($"{__instance.myPlayer.Data.PlayerName} Has been booted from vent: {ventId}, as {__instance.myPlayer.Data.RoleType.GetRoleName()}", "EventLog"); } [HarmonyPatch(typeof(PlayerPhysics), nameof(PlayerPhysics.CoEnterVent))] @@ -171,7 +171,7 @@ internal static class PlayerControlPatch private static void PlayerPhysics_CoEnterVent_Postfix(PlayerPhysics __instance, int id) { // Log vent entry events - Logger_.LogPrivate($"{__instance.myPlayer.Data.PlayerName} Has entered vent: {id}, as {__instance.myPlayer.Data.RoleType.GetRoleName()}", "EventLog"); + BAUPlugin.Logger.LogPrivate($"{__instance.myPlayer.Data.PlayerName} Has entered vent: {id}, as {__instance.myPlayer.Data.RoleType.GetRoleName()}", "EventLog"); } [HarmonyPatch(typeof(PlayerPhysics), nameof(PlayerPhysics.CoExitVent))] @@ -179,6 +179,6 @@ internal static class PlayerControlPatch private static void PlayerPhysics_CoExitVent_Postfix(PlayerPhysics __instance, int id) { // Log vent exit events - Logger_.LogPrivate($"{__instance.myPlayer.Data.PlayerName} Has exit vent: {id}, as {__instance.myPlayer.Data.RoleType.GetRoleName()}", "EventLog"); + BAUPlugin.Logger.LogPrivate($"{__instance.myPlayer.Data.PlayerName} Has exit vent: {id}, as {__instance.myPlayer.Data.RoleType.GetRoleName()}", "EventLog"); } } \ No newline at end of file diff --git a/src/Patches/Gameplay/Player/PlayerJoinAndLeftPatch.cs b/src/Patches/Gameplay/Player/PlayerJoinAndLeftPatch.cs index f8ed100..f316025 100644 --- a/src/Patches/Gameplay/Player/PlayerJoinAndLeftPatch.cs +++ b/src/Patches/Gameplay/Player/PlayerJoinAndLeftPatch.cs @@ -30,7 +30,7 @@ internal static class PlayerJoinAndLeftPatch host?.SetColor(host.CurrentOutfit.ColorId); } - Logger_.Log($"Successfully joined {GameCode.IntToGameName(AmongUsClient.Instance.GameId)}", "OnGameJoinedPatch"); + BAUPlugin.Logger.Log($"Successfully joined {GameCode.IntToGameName(AmongUsClient.Instance.GameId)}", "OnGameJoinedPatch"); } [HarmonyPatch(typeof(AmongUsClient), nameof(AmongUsClient.OnPlayerJoined))] @@ -153,7 +153,7 @@ internal static class PlayerJoinAndLeftPatch { var ReasonText = $"{playerData.ExtendedData().RealName} {forceReasonText}"; - Logger_.Log(ReasonText); + BAUPlugin.Logger.Log(ReasonText); HudManager.Instance.Notifier.AddDisconnectMessage(ReasonText); } @@ -190,7 +190,7 @@ internal static class PlayerJoinAndLeftPatch break; } - Logger_.Log(ReasonText); + BAUPlugin.Logger.Log(ReasonText); // Add formatted disconnect message to game UI HudManager.Instance.Notifier.AddDisconnectMessage(ReasonText); diff --git a/src/Patches/Gameplay/UI/Chat/ChatPatch.cs b/src/Patches/Gameplay/UI/Chat/ChatPatch.cs index 713a1fa..ddd7dd9 100644 --- a/src/Patches/Gameplay/UI/Chat/ChatPatch.cs +++ b/src/Patches/Gameplay/UI/Chat/ChatPatch.cs @@ -217,7 +217,7 @@ internal static class ChatPatch private static void ChatController_AddChat_Postfix(ChatController __instance, PlayerControl sourcePlayer, string chatText) { // Log chat publicly if player is alive, privately if dead - Logger_.LogPrivate($"{sourcePlayer.Data.PlayerName} -> {chatText}", "ChatLog"); + BAUPlugin.Logger.LogPrivate($"{sourcePlayer.Data.PlayerName} -> {chatText}", "ChatLog"); } [HarmonyPatch(typeof(ChatController), nameof(ChatController.SetChatBubbleName))] diff --git a/src/Patches/Gameplay/UI/GameSummaryPatch.cs b/src/Patches/Gameplay/UI/GameSummaryPatch.cs index b6c258b..09b8874 100644 --- a/src/Patches/Gameplay/UI/GameSummaryPatch.cs +++ b/src/Patches/Gameplay/UI/GameSummaryPatch.cs @@ -35,8 +35,8 @@ internal static class GameSummaryPatch // Log game end info to console private static void LogGameEnd() { - Logger_.LogHeader($"Game Has Ended - {Enum.GetName(typeof(MapNames), GameState.GetActiveMapId)}/{GameState.GetActiveMapId}", "GamePlayManager"); - Logger_.LogHeader("Game Summary Start", "GameSummary"); + BAUPlugin.Logger.LogHeader($"Game Has Ended - {Enum.GetName(typeof(MapNames), GameState.GetActiveMapId)}/{GameState.GetActiveMapId}", "GamePlayManager"); + BAUPlugin.Logger.LogHeader("Game Summary Start", "GameSummary"); } // Creates the visual game summary on screen @@ -52,14 +52,14 @@ internal static class GameSummaryPatch // Get win condition info var (winTeam, winTag, winColor) = GetWinInfo(); - Logger_.Log($"{winTeam}: {winTag}", "GameSummary"); + BAUPlugin.Logger.Log($"{winTeam}: {winTag}", "GameSummary"); // Build and display summary text var summaryHeader = BuildSummaryHeader(winTeam, winTag, winColor); var playerList = BuildPlayerList(); summaryText.text = $"{summaryHeader}\n\n{playerList}"; - Logger_.LogHeader("Game Summary End", "GameSummary"); + BAUPlugin.Logger.LogHeader("Game Summary End", "GameSummary"); } // Creates the text object for the summary @@ -191,7 +191,7 @@ internal static class GameSummaryPatch var playerLine = BuildPlayerLine(playerData); stringBuilder.AppendLine($"- {playerLine}\n"); - Logger_.Log(Utils.RemoveHtmlText(playerLine).Replace("\n", " "), "GameSummary"); + BAUPlugin.Logger.Log(Utils.RemoveHtmlText(playerLine).Replace("\n", " "), "GameSummary"); } return stringBuilder; diff --git a/src/Patches/Gameplay/UI/MeetingHudPatch.cs b/src/Patches/Gameplay/UI/MeetingHudPatch.cs index 60274e3..e5e353e 100644 --- a/src/Patches/Gameplay/UI/MeetingHudPatch.cs +++ b/src/Patches/Gameplay/UI/MeetingHudPatch.cs @@ -54,7 +54,7 @@ internal static class MeetingHudPatch MeetingHud.Instance.ProceedButton.DestroyTextTranslators(); } - Logger_.LogHeader("Meeting Has Started"); + BAUPlugin.Logger.LogHeader("Meeting Has Started"); } // Updates host icon with current host info @@ -134,6 +134,6 @@ internal static class MeetingHudPatch } timeOpen = 0f; - Logger_.LogHeader("Meeting Has Ended"); + BAUPlugin.Logger.LogHeader("Meeting Has Ended"); } } \ No newline at end of file diff --git a/src/Patches/Unity/UnityWebRequestPatch.cs b/src/Patches/Unity/UnityWebRequestPatch.cs index deabc9d..24d7a09 100644 --- a/src/Patches/Unity/UnityWebRequestPatch.cs +++ b/src/Patches/Unity/UnityWebRequestPatch.cs @@ -79,7 +79,7 @@ internal static class UnityWebRequestPatch if (responseHeader != null) { - Logger_.Log("Connected to a supported Better Among Us matchmaking server"); + BAUPlugin.Logger.Log("Connected to a supported Better Among Us matchmaking server"); } })); } diff --git a/src/Utilities/Utils.cs b/src/Utilities/Utils.cs index 759d89a..92fd053 100644 --- a/src/Utilities/Utils.cs +++ b/src/Utilities/Utils.cs @@ -431,7 +431,7 @@ internal static class Utils } catch (Exception ex) { - Logger_.Error(ex); + BAUPlugin.Logger.Error(ex); return null; } } @@ -461,7 +461,7 @@ internal static class Utils } catch (Exception ex) { - Logger_.Error(ex); + BAUPlugin.Logger.Error(ex); return null; } } @@ -475,13 +475,13 @@ internal static class Utils { if (!File.Exists(filePath)) { - Logger_.Error($"File not found: {filePath}"); + BAUPlugin.Logger.Error($"File not found: {filePath}"); return null; } if (Path.GetExtension(filePath).ToLower() != ".wav") { - Logger_.Error("Only .wav files are supported."); + BAUPlugin.Logger.Error("Only .wav files are supported."); return null; } @@ -497,7 +497,7 @@ internal static class Utils } catch (Exception ex) { - Logger_.Error($"Failed to load WAV: {ex}"); + BAUPlugin.Logger.Error($"Failed to load WAV: {ex}"); return null; } } From f6182d4d768c988c5f51e8e87257239e551c94cb Mon Sep 17 00:00:00 2001 From: D1GQ Date: Sun, 6 Sep 2026 23:24:13 -0500 Subject: [PATCH 17/19] Do not patch region drop down menu on Starlight, Starlight automatically does this --- src/Patches/Gameplay/UI/ServerDropdownPatch.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Patches/Gameplay/UI/ServerDropdownPatch.cs b/src/Patches/Gameplay/UI/ServerDropdownPatch.cs index 59e539e..5688e1f 100644 --- a/src/Patches/Gameplay/UI/ServerDropdownPatch.cs +++ b/src/Patches/Gameplay/UI/ServerDropdownPatch.cs @@ -1,9 +1,9 @@ using BetterAmongUs.Modules.Support; +using BetterAmongUs.Utilities.Extension; using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes.Arrays; using UnityEngine; using UnityEngine.SceneManagement; -using BetterAmongUs.Utilities.Extension; namespace BetterAmongUs.Patches.Gameplay.UI; @@ -14,6 +14,9 @@ internal static class ServerDropdownPatch [HarmonyPrefix] private static void FindAGameManager_Start_Prefix(FindAGameManager __instance) { + if (BAUPlugin.ModInfo.Starlight) + return; + if (BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_ServerDropDown)) return; @@ -33,7 +36,11 @@ internal static class ServerDropdownPatch [HarmonyPrefix] private static bool ServerDropdown_FillServerOptions_Prefix(ServerDropdown __instance) { - if (BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_ServerDropDown)) return true; + if (BAUPlugin.ModInfo.Starlight) + return true; + + if (BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_ServerDropDown)) + return true; __instance.background.size = new Vector2(5, 1); From 9d1f6fc0840ee1c4a3c79db6b9a86a89428b2644 Mon Sep 17 00:00:00 2001 From: D1GQ Date: Sun, 6 Sep 2026 23:30:09 -0500 Subject: [PATCH 18/19] Add protection to vent kick exploit --- .../NetObjectHandlers/UpdateSystemHandler.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Modules/AntiCheat/RPCHandlers/NetObjectHandlers/UpdateSystemHandler.cs b/src/Modules/AntiCheat/RPCHandlers/NetObjectHandlers/UpdateSystemHandler.cs index b66d2ce..7052b28 100644 --- a/src/Modules/AntiCheat/RPCHandlers/NetObjectHandlers/UpdateSystemHandler.cs +++ b/src/Modules/AntiCheat/RPCHandlers/NetObjectHandlers/UpdateSystemHandler.cs @@ -20,8 +20,8 @@ internal sealed class UpdateSystemHandler : RPCHandler { systemHandlers = new Dictionary> { - { (uint)SystemTypes.Sabotage, (sender, system, reader, count) => HandleSabotageSystem(sender, system.Cast(), reader) }, - { (uint)SystemTypes.Ventilation, (sender, system, reader, count) => HandleVentilationSystem(sender, system.Cast(), count) }, + { (uint)SystemTypes.Sabotage, (sender, system, reader, count) => HandleSabotageSystem(sender, system.Cast()) }, + { (uint)SystemTypes.Ventilation, (sender, system, reader, count) => HandleVentilationSystem(sender, system.Cast(), reader) }, { (uint)SystemTypes.Electrical, (sender, system, reader, count) => HandleSwitchSystem(sender, system.Cast(), count) }, { (uint)SystemTypes.Comms, (sender, system, reader, count) => HandleCommsSystem(sender, system, count) }, { (uint)SystemTypes.MushroomMixupSabotage, (sender, system, reader, count) => HandleMushroomMixupSabotageSystem(sender, system.Cast(), count) }, @@ -68,10 +68,8 @@ internal sealed class UpdateSystemHandler : RPCHandler return true; } - private static bool HandleSabotageSystem(PlayerControl? sender, SabotageSystemType sabotageSystem, MessageReader reader) + private static bool HandleSabotageSystem(PlayerControl? sender, SabotageSystemType sabotageSystem) { - byte count = reader.ReadByte(); - if (!sender.IsImpostorTeam()) { return false; @@ -85,8 +83,14 @@ internal sealed class UpdateSystemHandler : RPCHandler return true; } - private static bool HandleVentilationSystem(PlayerControl? sender, VentilationSystem ventilationSystem, byte count) + private static bool HandleVentilationSystem(PlayerControl? sender, VentilationSystem ventilationSystem, MessageReader reader) { + _ = reader.ReadUInt16(); + var operation = (VentilationSystem.Operation)reader.ReadByte(); + if (operation == VentilationSystem.Operation.BootImpostors) + { + return false; + } return true; } From 3e14546d8fd5e55f25e013bfdb57f30f66c28163 Mon Sep 17 00:00:00 2001 From: D1GQ Date: Tue, 29 Sep 2026 13:55:02 -0500 Subject: [PATCH 19/19] Update to v19 --- src/BAUPlugin.ModInfo.cs | 2 +- src/BetterAmongUs.csproj | 2 +- src/Patches/Gameplay/Options/LogicOptionsPatch.cs | 5 ++--- src/Patches/Gameplay/ZoomPatch.cs | 5 ++--- src/Utilities/PlayerControlUtils.cs | 2 +- src/Utilities/RoleUtils.cs | 3 ++- 6 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/BAUPlugin.ModInfo.cs b/src/BAUPlugin.ModInfo.cs index 696f546..d02c968 100644 --- a/src/BAUPlugin.ModInfo.cs +++ b/src/BAUPlugin.ModInfo.cs @@ -64,7 +64,7 @@ internal partial class BAUPlugin /// internal static string[] SupportedAmongUsVersions = [ - "2026.8.18" + "2026.9.29" ]; /// diff --git a/src/BetterAmongUs.csproj b/src/BetterAmongUs.csproj index 1dc25e9..3620d07 100644 --- a/src/BetterAmongUs.csproj +++ b/src/BetterAmongUs.csproj @@ -80,7 +80,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Patches/Gameplay/Options/LogicOptionsPatch.cs b/src/Patches/Gameplay/Options/LogicOptionsPatch.cs index 025fe9c..0e0037d 100644 --- a/src/Patches/Gameplay/Options/LogicOptionsPatch.cs +++ b/src/Patches/Gameplay/Options/LogicOptionsPatch.cs @@ -1,5 +1,4 @@ -using AmongUs.GameOptions; -using BetterAmongUs.Utilities; +using BetterAmongUs.Utilities; using HarmonyLib; namespace BetterAmongUs.Patches.Gameplay.Options; @@ -15,7 +14,7 @@ internal static class LogicOptionsPatch return; // Show anonymous votes when dead and not Guardian Angel - if (!PlayerControl.LocalPlayer.IsAlive() && !PlayerControl.LocalPlayer.Is(RoleTypes.GuardianAngel)) + if (!PlayerControl.LocalPlayer.IsAlive() && !PlayerControl.LocalPlayer.IsGhostRole()) { __result = false; } diff --git a/src/Patches/Gameplay/ZoomPatch.cs b/src/Patches/Gameplay/ZoomPatch.cs index 3835cc1..2f3d0c2 100644 --- a/src/Patches/Gameplay/ZoomPatch.cs +++ b/src/Patches/Gameplay/ZoomPatch.cs @@ -1,6 +1,5 @@ -using AmongUs.GameOptions; +using BetterAmongUs.Modules; using BetterAmongUs.Utilities; -using BetterAmongUs.Modules; using HarmonyLib; using UnityEngine; @@ -21,7 +20,7 @@ internal class ZoomPatch // - Not Guardian Angel AND // - Either not in gameplay OR player is dead bool canZoom = GameState.IsCanMove && - !PlayerControl.LocalPlayer.Is(RoleTypes.GuardianAngel) && + !PlayerControl.LocalPlayer.IsGhostRole() && (!GameState.IsInGamePlay || !PlayerControl.LocalPlayer.IsAlive()); // Should we reset zoom (when player was zooming but no longer can) diff --git a/src/Utilities/PlayerControlUtils.cs b/src/Utilities/PlayerControlUtils.cs index 709b281..49ee12d 100644 --- a/src/Utilities/PlayerControlUtils.cs +++ b/src/Utilities/PlayerControlUtils.cs @@ -468,7 +468,7 @@ internal static class PlayerControlUtils if (data == null) return false; - return data.RoleType == RoleTypes.GuardianAngel; + return data.RoleType is RoleTypes.GuardianAngel or RoleTypes.SpiritGuide; } /// diff --git a/src/Utilities/RoleUtils.cs b/src/Utilities/RoleUtils.cs index 2e134ab..a0ed5cf 100644 --- a/src/Utilities/RoleUtils.cs +++ b/src/Utilities/RoleUtils.cs @@ -114,7 +114,7 @@ internal static class RoleUtils return string.Empty; } - if (PlayerControl.LocalPlayer.Is(RoleTypes.GuardianAngel)) + if (PlayerControl.LocalPlayer.IsGhostRole()) { return string.Empty; } @@ -146,6 +146,7 @@ internal static class RoleUtils { { RoleTypes.CrewmateGhost, Colors.CrewmateBlue.ColorToHex() }, { RoleTypes.GuardianAngel, "#8cffff" }, + { RoleTypes.SpiritGuide, "#ff0066" }, { RoleTypes.Crewmate, Colors.CrewmateBlue.ColorToHex() }, { RoleTypes.Scientist, "#00d9d9" }, { RoleTypes.Engineer, "#8f8f8f" },