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" },