Merge pull request #135 from D1GQ/v3.3.4-dev

V3.3.4 - AU v19 Compatibility
This commit is contained in:
D1GQ authored and GitHub committed 2026-09-29 15:39:22 -05:00
commit 23f8f5dc4a
87 files changed
+1340 -920

No files matched your search

+2 -2
View File
@@ -15,7 +15,7 @@ internal abstract class AutoRegisterAttribute : Attribute
/// </summary>
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<T> : AutoRegisterAttribute where T
/// <inheritdoc/>
protected override void Register()
{
var types = ModInfo.Assembly.GetTypes();
var types = BAUPlugin.ModInfo.Assembly.GetTypes();
foreach (var type in types)
{
+3 -4
View File
@@ -1,5 +1,4 @@
using BetterAmongUs.Modules;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.Injection;
using System.Reflection;
namespace BetterAmongUs.Attributes;
@@ -21,7 +20,7 @@ internal class RegisterInIl2Cpp(params Type[] interfaces) : Attribute
/// </summary>
internal static void Initialize()
{
var types = ModInfo.Assembly.GetTypes();
var types = BAUPlugin.ModInfo.Assembly.GetTypes();
foreach (var type in types)
{
var attr = type.GetCustomAttribute<RegisterInIl2Cpp>();
@@ -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}");
}
}
}
+77
View File
@@ -0,0 +1,77 @@
namespace BetterAmongUs;
internal partial class BAUPlugin
{
/// <summary>
/// Contains constants That are used across BAU.
/// </summary>
internal static class Constants
{
/// <summary>
/// Example comment for BanPlayerList file.
/// </summary>
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
""";
/// <summary>
/// Example comment for BanNameList file.
/// </summary>
internal const string BAN_NAME_LIST_CONTENT = """
// Example banned player name regex patterns
// Each pattern on a new line
//
// ^TNT$
// ^hyde$
// ^[\P{IsBasicLatin}\s]+$
""";
/// <summary>
/// Example comment for BanChatList file.
/// </summary>
internal const string BAN_CHAT_LIST_CONTENT = """
// Example banned chat message regex patterns
// Each pattern on a new line
//
// (?i)(?<!(?:say|said|don'?t|didn'?t|wr[io]te|\bthe\b|\bat\b).*)(?: |^)sta*r?t*(?:ing|\b)(?!.*ban)
// (?i)^go[go]*$");
// (?i)ni?gg?(?:a|er)
// (?i)sns(?-i).*[A-Z]{6}
// [A-Z]{6}.*(?i)sns
// (?i)(?:modded|expert|no cooldown) lobby(?-i).*[A-Z]{6}
// [A-Z]{6}.*(?i)(?:modded|expert|no cooldown) lobby
""";
/// <summary>
/// Legacy example comment for BanNameList file.
/// </summary>
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 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";
}
}
+125
View File
@@ -0,0 +1,125 @@
using Semver;
using System.Reflection;
namespace BetterAmongUs;
internal partial class BAUPlugin
{
/// <summary>
/// Contains metadata and constants for the BetterAmongUs mod.
/// </summary>
internal static class ModInfo
{
/// <summary>
/// The base version number of the mod in MAJOR.MINOR.PATCH format.
/// </summary>
internal const string VERSION_NUMBER = "1.3.4";
/// <summary>
/// The beta release number. Increment this for each beta release.
/// </summary>
internal const string BETA_NUMBER = "0";
/// <summary>
/// Gets the full version string for the current build configuration.
/// </summary>
#if RELEASE
internal const string VERSION = VERSION_NUMBER;
#elif BETA
internal const string VERSION = $"{VERSION_NUMBER}-beta-{BETA_NUMBER}";
#endif
/// <summary>
/// Gets the full version string with a v prefix for the current build configuration.
/// </summary>
internal const string VERSION_STRING = "v" + VERSION;
/// <summary>
/// Gets the parsed semantic version of the mod.
/// </summary>
internal static readonly SemVersion SemVersion = SemVersion.Parse(VERSION);
/// <summary>
/// Gets the Git commit hash from assembly metadata.
/// </summary>
public static string CommitHash = ThisAssembly.Git.Commit;
/// <summary>
/// Gets the build date from assembly metadata.
/// </summary>
public static string BuildDate = ThisAssembly.Metadata.BuildDate;
/// <summary>
/// The name of BAU.
/// </summary>
internal const string PLUGIN_NAME = "BetterAmongUs";
/// <summary>
/// The GUID (Globally Unique Identifier) of BAU.
/// </summary>
internal const string PLUGIN_GUID = "com.d1gq.betteramongus";
/// <summary>
/// Gets the list of supported Among Us versions.
/// </summary>
internal static string[] SupportedAmongUsVersions =
[
"2026.9.29"
];
/// <summary>
/// The GitHub repository URL for BAU.
/// </summary>
internal const string GITHUB = ThisAssembly.Git.RepositoryUrl;
/// <summary>
/// The Discord invite URL for BAU.
/// </summary>
internal const string DISCORD = "https://discord.gg/vjYrXpzNAn";
/// <summary>
/// Indicator rather that BAU is running on Starlight for Android.
/// </summary>
internal static readonly bool Starlight = OperatingSystem.IsAndroid();
/// <summary>
/// Retrieves metadata from the assembly attributes.
/// </summary>
/// <param name="key">The metadata key to retrieve.</param>
/// <returns>The metadata value, or an empty string if not found.</returns>
private static string GetAssemblyMetadata(string key)
{
var attribute = Assembly
.GetCustomAttributes<AssemblyMetadataAttribute>()
.FirstOrDefault(a => a.Key == key);
return attribute?.Value ?? string.Empty;
}
/// <summary>
/// The assembly associated to this mod.
/// </summary>
internal static Assembly Assembly
{
get
{
if (field == null)
{
field = Assembly.GetExecutingAssembly();
}
return field;
}
}
/// <summary>
/// Contains constants for Among Us.
/// </summary>
internal static class AmongUs
{
/// <summary>
/// The process name of the Among Us executable.
/// </summary>
internal const string PROCESS_NAME = "Among Us.exe";
}
}
}
+16 -51
View File
@@ -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 class BAUPlugin : BasePlugin
internal partial class BAUPlugin : BasePlugin
{
/// <summary>
/// Gets the formatted version text for display.
/// </summary>
/// <param name="newLine">Whether to use newline separation for additional info.</param>
/// <returns>Formatted version string.</returns>
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;
}
/// <summary>
/// Gets the BAUPlugin instance.
/// </summary>
@@ -68,11 +35,6 @@ internal class BAUPlugin : BasePlugin
/// </summary>
internal static Harmony Harmony { get; } = new Harmony(ModInfo.PLUGIN_GUID);
/// <summary>
/// Gets the BetterAmongUs version string.
/// </summary>
internal static string BetterAmongUsVersion => ModInfo.PLUGIN_VERSION;
/// <summary>
/// Gets the application version string.
/// </summary>
@@ -86,7 +48,7 @@ internal class BAUPlugin : BasePlugin
/// <summary>
/// Gets platform-specific data.
/// </summary>
internal static PlatformSpecificData PlatformData => Constants.GetPlatformData();
internal static PlatformSpecificData PlatformData => global::Constants.GetPlatformData();
/// <summary>
/// Gets the list of all PlayerControl instances.
@@ -108,10 +70,15 @@ internal class BAUPlugin : BasePlugin
/// </summary>
internal static Vent[] AllVents => UnityEngine.Object.FindObjectsOfType<Vent>();
/// <summary>
/// Gets the BAU logger instance.
/// </summary>
internal static BAULogger Logger { get; private set; } = null!;
/// <summary>
/// Gets the BepInEx logger instance.
/// </summary>
internal static ManualLogSource? Logger;
private static ManualLogSource? _manualLogSource;
public override void Load()
{
@@ -138,7 +105,7 @@ internal class BAUPlugin : BasePlugin
}
catch (Exception ex)
{
Logger_.Error(ex);
Logger.Error(ex);
}
}
@@ -163,14 +130,10 @@ internal class BAUPlugin : BasePlugin
OutfitData.Initialize();
SceneManager.add_sceneLoaded((Action<Scene, LoadSceneMode>)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 {BetterAmongUsVersion}-{ModInfo.BuildDate} - [{AppVersion} --> {SupportedVersions}] {Utils.GetPlatformName(PlatformData.Platform)}");
Logger.Log($"BetterAmongUs {ModInfo.VERSION_STRING}-{ModInfo.BuildDate} - [{AppVersion} --> {SupportedVersions}] {Utils.GetPlatformName(PlatformData.Platform)}");
}
/// <summary>
@@ -202,13 +165,15 @@ internal class BAUPlugin : BasePlugin
/// </summary>
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'--------------------------------------------------------------------------------'");
+7 -2
View File
@@ -6,6 +6,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<OutputType>Library</OutputType>
<LangVersion>preview</LangVersion>
<Configurations>Debug;Release;Beta</Configurations>
</PropertyGroup>
<!-- Apply Null Notation Restrictions -->
@@ -79,21 +80,25 @@
<!-- NuGet Core Packages -->
<ItemGroup>
<PackageReference Include="AmongUs.GameLibs.Steam" Version="2026.8.18" />
<PackageReference Include="AmongUs.GameLibs.Steam" Version="2026.9.29" />
<PackageReference Include="BepInEx.IL2CPP.MSBuild" Version="2.0.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="BepInEx.Unity.IL2CPP" Version="6.0.0-be.735" Private="false" ExcludeAssets="runtime;native" />
<PackageReference Include="Costura.Fody" Version="5.8.0-alpha0098">
<IncludeAssets>runtime; compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<!-- NuGet Info Packages -->
<ItemGroup>
<PackageReference Include="Semver" Version="3.0.0" />
<PackageReference Include="GitInfo" Version="3.3.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="ThisAssembly.Metadata" Version="2.1.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+3
View File
@@ -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
-4
View File
@@ -36,8 +36,6 @@ internal sealed class PlayerArgument(BaseCommand command, string argInfo = "{pla
/// <summary>
/// Tries to parse the player argument and find the corresponding PlayerControl.
/// </summary>
/// <param name="result"></param>
/// <returns></returns>
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
/// <summary>
/// Checks if the given player info matches the argument value.
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private bool IsMatchingPlayer(NetworkedPlayerInfo data)
{
return Arg.Equals(data.PlayerName.Replace(' ', '_'), StringComparison.OrdinalIgnoreCase)
@@ -0,0 +1,76 @@
using BetterAmongUs.Utilities;
namespace BetterAmongUs.Commands.Arguments;
/// <summary>
/// Represents a player command argument.
/// </summary>
/// <param name="command">The command this argument belongs to.</param>
/// <param name="argInfo">Information about the argument (default: "{player}").</param>
internal sealed class PlayerInfoArgument(BaseCommand command, string argInfo = "{player}") : BaseArgument<NetworkedPlayerInfo>(command, argInfo)
{
protected override string[] GetArgSuggestions()
{
List<string> 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];
}
/// <summary>
/// Tries to parse the player argument and find the corresponding PlayerControl.
/// </summary>
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;
}
/// <summary>
/// Checks if the given player info matches the argument value.
/// </summary>
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);
}
}
+27 -47
View File
@@ -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 (!ModInfo.Starlight)
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.GetVersionText().Replace(' ', '-').ToLower() + "-" + 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.GetVersionText().Replace(' ', '-').ToLower() + "-" + 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.GetVersionText().Replace(' ', '-').ToLower() + "-" + 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.GetVersionText().Replace(' ', '-').ToLower() + "-" + 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 <color=#b1b1b1>'{logFolderPath}'</color>");
}
}
+14 -10
View File
@@ -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($"<size=150%><color={hexColor}><b>{player.Data.PlayerName}</color> Info:</b></size>\n");
sb.Append($"{format1} <color=#c1c1c1>ID: {player.Data.PlayerId}</color>\n");
sb.Append($"{format2} <color=#c1c1c1>HashPUID: {Utils.GetHashStr($"{player.Data.Puid}")}</color>\n");
sb.Append($"{format2} <color=#c1c1c1>Platform: {Utils.GetPlatformName(player)}</color>\n");
sb.Append($"{format3} <color=#c1c1c1>FriendCode: {player.Data.FriendCode}</color>");
sb.Append($"<size=150%><color={hexColor}><b>{playerData.PlayerName}</color> Info:</b></size>\n");
sb.Append($"{format1} <color=#c1c1c1>ID: {playerData.PlayerId}</color>\n");
sb.Append($"{format2} <color=#c1c1c1>HashPUID: {Utils.GetHashStr($"{playerData.Puid}")}</color>\n");
var client = Utils.ClientFromClientId(playerData.ClientId);
if (client != null)
{
sb.Append($"{format2} <color=#c1c1c1>Platform: {Utils.GetPlatformName(client.PlatformData.Platform)}</color>\n");
}
sb.Append($"{format3} <color=#c1c1c1>FriendCode: {playerData.FriendCode}</color>");
CommandResultText(sb.ToString());
}
}
+1 -3
View File
@@ -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];
}
+162 -66
View File
@@ -1,6 +1,7 @@
using BetterAmongUs.Data.Config;
using BetterAmongUs.Data.Json;
using BetterAmongUs.Utilities;
using System.Text.RegularExpressions;
using UnityEngine;
namespace BetterAmongUs.Data;
@@ -64,12 +65,12 @@ internal static class BetterDataManager
/// <summary>
/// The log file path.
/// </summary>
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");
/// <summary>
/// The previous log file path.
/// </summary>
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");
/// <summary>
/// Legacy data file path (BetterData.json).
@@ -102,9 +103,14 @@ internal static class BetterDataManager
internal static readonly string banNameListFilePath = Path.Combine(Folders.saveInfoFolderPath, "BanNameList.txt");
/// <summary>
/// File containing banned words/patterns.
/// Legacy file containing banned words/patterns.
/// </summary>
internal static readonly string banWordListFilePath = Path.Combine(Folders.saveInfoFolderPath, "BanWordList.txt");
internal static readonly string banWordListFilePath_Legacy = Path.Combine(Folders.saveInfoFolderPath, "BanWordList.txt");
/// <summary>
/// File containing banned chat messages/patterns.
/// </summary>
internal static readonly string banChatListFilePath = Path.Combine(Folders.saveInfoFolderPath, "BanChatList.txt");
}
/// <summary>
@@ -114,7 +120,7 @@ internal static class BetterDataManager
[
Files.banPlayerListFilePath,
Files.banNameListFilePath,
Files.banWordListFilePath
Files.banChatListFilePath
];
/// <summary>
@@ -123,7 +129,7 @@ internal static class BetterDataManager
/// <returns>The game installation path string.</returns>
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!");
}
@@ -148,7 +154,7 @@ internal static class BetterDataManager
/// <returns>The persistent data path string.</returns>
internal static string GetPathToAmongUsData()
{
if (!ModInfo.Starlight)
if (!BAUPlugin.ModInfo.Starlight)
{
return Application.persistentDataPath;
}
@@ -169,63 +175,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(BAUPlugin.Constants.BAN_PLAYER_LIST_CONTENT);
}
else if (path == Files.banNameListFilePath)
{
writer.WriteLine(BAUPlugin.Constants.BAN_NAME_LIST_CONTENT);
}
else if (path == Files.banChatListFilePath)
{
writer.WriteLine(BAUPlugin.Constants.BAN_CHAT_LIST_CONTENT);
}
}
}
@@ -235,16 +204,143 @@ internal static class BetterDataManager
/// </summary>
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))
{
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();
}
/// <summary>
/// Performs data migration from legacy wildcard‑based ban lists to the new regex‑based format.
/// </summary>
private static void HandleMigrationFromWildcardToRegex()
{
MigrateBanWordList();
MigrateBanNameList();
}
/// <summary>
/// Migrates the legacy wildcard‑based BanWordList to the new regex-based BanChatList.
/// </summary>
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(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");
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);
}
/// <summary>
/// Migrates the BanNameList from the legacy wildcard format to the current regex‑based format.
/// </summary>
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(BAUPlugin.Constants.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(BAUPlugin.Constants.BAN_NAME_LIST_CONTENT_LEGACY)) return;
using var writer = File.CreateText(Files.banNameListFilePath);
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");
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();
}
}
/// <summary>
/// Determines whether any line from the supplied array matches a line in the specified content string.
/// </summary>
/// <param name="lines">An array of strings to search.</param>
/// <param name="content">A multi‑line string whose individual lines are compared against <paramref name="lines"/>.</param>
/// <returns><c>true</c> if at least one line from <paramref name="lines"/> equals any line in <paramref name="content"/>, <c>false</c> otherwise.</returns>
private static bool AnyLineInContentLines(this string[] lines, string content)
{
return content.Split(Environment.NewLine).Any(lines.Contains);
}
/// <summary>
+10
View File
@@ -63,11 +63,21 @@ internal static class BAUConfigs
/// </summary>
internal static BAUConfigEntry<bool> MinimapIcons { get; } = new("Better Options", "MinimapIcons", true);
/// <summary>
/// Gets the configuration entry for new minimap colors setting.
/// </summary>
internal static BAUConfigEntry<bool> BetterMinimapColors { get; } = new("Better Options", "BetterMinimapColors", true);
/// <summary>
/// Gets the configuration entry for vent color groups setting.
/// </summary>
internal static BAUConfigEntry<bool> VentColorGroups { get; } = new("Better Options", "VentColorGroups", true);
/// <summary>
/// Gets the configuration entry for better color blind text setting.
/// </summary>
internal static BAUConfigEntry<bool> BetterColorblindText { get; } = new("Better Options", "BetterColorblindText", true);
/// <summary>
/// Gets the configuration entry for compress settings file setting.
/// </summary>
+2 -2
View File
@@ -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;
}
}
+1 -1
View File
@@ -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}");
}
}
}
+1 -1
View File
@@ -38,7 +38,7 @@ internal sealed class AppearReplayEvent : IReplayEvent<AppearReplayEvent.AppearR
EventData = new AppearReplayData(args.Player.PlayerId, args.Animate);
}
internal record AppearReplayData(int PlayerId, bool Animate) : IReplayEvent.Data;
internal record AppearReplayData(byte PlayerId, bool Animate) : IReplayEvent.Data;
internal record AppearReplayArgs(PlayerControl Player, bool Animate) : IReplayEvent.Args;
}
+1 -1
View File
@@ -37,7 +37,7 @@ internal sealed class ChatEvent : IReplayEvent<ChatEvent.ChatReplayData, ChatEve
EventData = new ChatReplayData(args.Player.PlayerId, args.Message);
}
internal record ChatReplayData(int PlayerId, string Message) : IReplayEvent.Data;
internal record ChatReplayData(byte PlayerId, string Message) : IReplayEvent.Data;
internal record ChatReplayArgs(PlayerControl Player, string Message) : IReplayEvent.Args;
}
+1 -1
View File
@@ -37,7 +37,7 @@ internal sealed class ChatNoteEvent : IReplayEvent<ChatNoteEvent.ChatNoteReplayD
EventData = new ChatNoteReplayData(args.Player.PlayerId, (int)args.NoteType);
}
internal record ChatNoteReplayData(int PlayerId, int NoteType) : IReplayEvent.Data;
internal record ChatNoteReplayData(byte PlayerId, int NoteType) : IReplayEvent.Data;
internal record ChatNoteReplayArgs(PlayerControl Player, ChatNoteTypes NoteType) : IReplayEvent.Args;
}
+1 -1
View File
@@ -35,7 +35,7 @@ internal sealed class MurderReplayEvent : IReplayEvent<MurderReplayEvent.MurderR
EventData = new MurderReplayData(murderReplayArgs.Killer.PlayerId, murderReplayArgs.Target.PlayerId);
}
internal record MurderReplayData(int KillerId, int TargetId) : IReplayEvent.Data;
internal record MurderReplayData(byte KillerId, byte TargetId) : IReplayEvent.Data;
internal record MurderReplayArgs(PlayerControl Killer, PlayerControl Target) : IReplayEvent.Args;
}
+1 -1
View File
@@ -42,7 +42,7 @@ internal sealed class ProtectReplayEvent : IReplayEvent<ProtectReplayEvent.Prote
EventData = new ProtectReplayData(args.Player.PlayerId, args.Target.PlayerId);
}
internal record ProtectReplayData(int PlayerId, int TargetId) : IReplayEvent.Data;
internal record ProtectReplayData(byte PlayerId, byte TargetId) : IReplayEvent.Data;
internal record ProtectReplayArgs(PlayerControl Player, PlayerControl Target) : IReplayEvent.Args;
}
@@ -36,7 +36,7 @@ internal sealed class ProtectedReplayEvent : IReplayEvent<ProtectedReplayEvent.P
EventData = new ProtectedReplayData(args.Killer.PlayerId, args.Target.PlayerId);
}
internal record ProtectedReplayData(int KillerId, int TargetId) : IReplayEvent.Data;
internal record ProtectedReplayData(byte KillerId, byte TargetId) : IReplayEvent.Data;
internal record ProtectedReplayArgs(PlayerControl Killer, PlayerControl Target) : IReplayEvent.Args;
}
+1 -1
View File
@@ -36,7 +36,7 @@ internal sealed class SetRoleEvent : IReplayEvent<SetRoleEvent.SetRoleReplayData
EventData = new SetRoleReplayData(args.Player.PlayerId, (int)args.RoleType);
}
internal record SetRoleReplayData(int PlayerId, int RoleType) : IReplayEvent.Data;
internal record SetRoleReplayData(byte PlayerId, int RoleType) : IReplayEvent.Data;
internal record SetRoleReplayArgs(PlayerControl Player, RoleTypes RoleType) : IReplayEvent.Args;
}
@@ -42,7 +42,7 @@ internal sealed class ShapeshiftReplayEvent : IReplayEvent<ShapeshiftReplayEvent
EventData = new ShapeshiftReplayData(args.Player.PlayerId, args.Target.PlayerId, args.Animate);
}
internal record ShapeshiftReplayData(int PlayerId, int TargetId, bool Animate) : IReplayEvent.Data;
internal record ShapeshiftReplayData(byte PlayerId, byte TargetId, bool Animate) : IReplayEvent.Data;
internal record ShapeshiftReplayArgs(PlayerControl Player, PlayerControl Target, bool Animate) : IReplayEvent.Args;
}
@@ -37,7 +37,7 @@ internal sealed class UpdateSystemReplayEvent : IReplayEvent<UpdateSystemReplayE
EventData = new UpdateSystemReplayData(checked((byte)args.System), args.Player.PlayerId, args.Amount);
}
internal record UpdateSystemReplayData(byte SystemType, int PlayerId, byte Amount) : IReplayEvent.Data;
internal record UpdateSystemReplayData(byte SystemType, byte PlayerId, byte Amount) : IReplayEvent.Data;
internal record UpdateSystemReplayArgs(SystemTypes System, PlayerControl Player, byte Amount) : IReplayEvent.Args;
}
+1 -1
View File
@@ -38,7 +38,7 @@ internal sealed class VanishReplayEvent : IReplayEvent<VanishReplayEvent.VanishR
EventData = new VanishReplayData(args.Player.PlayerId);
}
internal record VanishReplayData(int PlayerId) : IReplayEvent.Data;
internal record VanishReplayData(byte PlayerId) : IReplayEvent.Data;
internal record VanishReplayArgs(PlayerControl Player) : IReplayEvent.Args;
}
+1 -1
View File
@@ -45,7 +45,7 @@ internal sealed class VentReplayEvent : IReplayEvent<VentReplayEvent.VentReplayD
EventData = new VentReplayData(args.Player.PlayerId, args.Exit, args.VentId);
}
internal record VentReplayData(int PlayerId, bool Exit, int VentId) : IReplayEvent.Data;
internal record VentReplayData(byte PlayerId, bool Exit, int VentId) : IReplayEvent.Data;
internal record VentReplayArgs(PlayerControl Player, bool Exit, int VentId) : IReplayEvent.Args;
}
+2 -2
View File
@@ -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 [];
}
}
+12
View File
@@ -0,0 +1,12 @@
namespace BetterAmongUs.Enums;
/// <summary>
/// Represents custom platform identifiers.
/// </summary>
internal enum CustomPlatforms
{
/// <summary>
/// Starlight.
/// </summary>
Starlight = 112
}
+8
View File
@@ -0,0 +1,8 @@
<!-- FodyWeavers.xml -->
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<Costura>
<IncludeAssemblies>
Semver
</IncludeAssemblies>
</Costura>
</Weavers>
+141
View File
@@ -0,0 +1,141 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="Costura" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:all>
<xs:element minOccurs="0" maxOccurs="1" name="ExcludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="IncludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="ExcludeRuntimeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="IncludeRuntimeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="Unmanaged32Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="Unmanaged64Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="PreloadOrder" type="xs:string">
<xs:annotation>
<xs:documentation>The order of preloaded assemblies, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:all>
<xs:attribute name="CreateTemporaryAssemblies" type="xs:boolean">
<xs:annotation>
<xs:documentation>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.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeDebugSymbols" type="xs:boolean">
<xs:annotation>
<xs:documentation>Controls if .pdbs for reference assemblies are also embedded.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeRuntimeReferences" type="xs:boolean">
<xs:annotation>
<xs:documentation>Controls if runtime assemblies are also embedded.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="UseRuntimeReferencePaths" type="xs:boolean">
<xs:annotation>
<xs:documentation>Controls whether the runtime assemblies are embedded with their full path or only with their assembly name.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="DisableCompression" type="xs:boolean">
<xs:annotation>
<xs:documentation>Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="DisableCleanup" type="xs:boolean">
<xs:annotation>
<xs:documentation>As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="LoadAtModuleInit" type="xs:boolean">
<xs:annotation>
<xs:documentation>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.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IgnoreSatelliteAssemblies" type="xs:boolean">
<xs:annotation>
<xs:documentation>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.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="ExcludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with |</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="ExcludeRuntimeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with |</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeRuntimeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="Unmanaged32Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="Unmanaged64Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="PreloadOrder" type="xs:string">
<xs:annotation>
<xs:documentation>The order of preloaded assemblies, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>
+3 -3
View File
@@ -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);
+2 -2
View File
@@ -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)
{
+3 -3
View File
@@ -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
/// </summary>
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;
}
-125
View File
@@ -1,125 +0,0 @@
using BetterAmongUs.Enums;
using System.Reflection;
namespace BetterAmongUs;
/// <summary>
/// Contains metadata and constants for the BetterAmongUs mod.
/// </summary>
internal static class ModInfo
{
/// <summary>
/// Gets the release type of the current build.
/// </summary>
internal static readonly ReleaseTypes ReleaseBuildType = ReleaseTypes.Release;
/// <summary>
/// Gets the Git commit hash from assembly metadata.
/// </summary>
public static string CommitHash = ThisAssembly.Git.Commit;
/// <summary>
/// Gets the build date from assembly metadata.
/// </summary>
public static string BuildDate = ThisAssembly.Metadata.BuildDate;
/// <summary>
/// The beta number for beta releases.
/// </summary>
internal const string BETA_NUM = "0";
/// <summary>
/// The hotfix number for hotfix releases.
/// </summary>
internal const string HOTFIX_NUM = "1";
/// <summary>
/// Indicates whether this is a hotfix release.
/// </summary>
internal const bool IS_HOTFIX = true;
/// <summary>
/// The name of BAU.
/// </summary>
internal const string PLUGIN_NAME = "BetterAmongUs";
/// <summary>
/// The GUID (Globally Unique Identifier) of BAU.
/// </summary>
internal const string PLUGIN_GUID = "com.d1gq.betteramongus";
/// <summary>
/// The version of BAU.
/// </summary>
internal const string PLUGIN_VERSION = "1.3.3";
/// <summary>
/// Gets the list of supported Among Us versions.
/// </summary>
internal static string[] SupportedAmongUsVersions =
[
"2026.8.18"
];
/// <summary>
/// The GitHub repository URL for BAU.
/// </summary>
internal const string GITHUB = ThisAssembly.Git.RepositoryUrl;
/// <summary>
/// The Discord invite URL for BAU.
/// </summary>
internal const string DISCORD = "https://discord.gg/vjYrXpzNAn";
/// <summary>
/// Indicator rather that BAU is running on Starlight for Android.
/// </summary>
internal static readonly bool Starlight = OperatingSystem.IsAndroid();
/// <summary>
/// Retrieves metadata from the assembly attributes.
/// </summary>
/// <param name="key">The metadata key to retrieve.</param>
/// <returns>The metadata value, or an empty string if not found.</returns>
private static string GetAssemblyMetadata(string key)
{
var attribute = Assembly
.GetCustomAttributes<AssemblyMetadataAttribute>()
.FirstOrDefault(a => a.Key == key);
return attribute?.Value ?? string.Empty;
}
/// <summary>
/// The assembly associated to this mod.
/// </summary>
internal static Assembly Assembly
{
get
{
if (field == null)
{
field = Assembly.GetExecutingAssembly();
}
return field;
}
}
/// <summary>
/// Contains constants for Among Us.
/// </summary>
internal static class AmongUs
{
/// <summary>
/// The process name of the Among Us executable.
/// </summary>
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";
}
}
+4 -4
View File
@@ -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;
}
}
+2 -2
View File
@@ -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}");
}
/// <summary>
@@ -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;
@@ -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}");
}
@@ -20,8 +20,8 @@ internal sealed class UpdateSystemHandler : RPCHandler
{
systemHandlers = new Dictionary<uint, Func<PlayerControl?, ISystemType, MessageReader, byte, bool>>
{
{ (uint)SystemTypes.Sabotage, (sender, system, reader, count) => HandleSabotageSystem(sender, system.Cast<SabotageSystemType>(), reader) },
{ (uint)SystemTypes.Ventilation, (sender, system, reader, count) => HandleVentilationSystem(sender, system.Cast<VentilationSystem>(), count) },
{ (uint)SystemTypes.Sabotage, (sender, system, reader, count) => HandleSabotageSystem(sender, system.Cast<SabotageSystemType>()) },
{ (uint)SystemTypes.Ventilation, (sender, system, reader, count) => HandleVentilationSystem(sender, system.Cast<VentilationSystem>(), reader) },
{ (uint)SystemTypes.Electrical, (sender, system, reader, count) => HandleSwitchSystem(sender, system.Cast<SwitchSystem>(), count) },
{ (uint)SystemTypes.Comms, (sender, system, reader, count) => HandleCommsSystem(sender, system, count) },
{ (uint)SystemTypes.MushroomMixupSabotage, (sender, system, reader, count) => HandleMushroomMixupSabotageSystem(sender, system.Cast<MushroomMixupSabotageSystem>(), 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;
}
@@ -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!");
}
}
}
@@ -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}'");
}
+214
View File
@@ -0,0 +1,214 @@
using BepInEx;
using BepInEx.Logging;
using BetterAmongUs.Utilities;
namespace BetterAmongUs.Modules;
/// <summary>
/// Provides logging for BetterAmongUs with various log levels and destinations.
/// </summary>
internal sealed class BAULogger(ManualLogSource manualLogSource)
{
private readonly ManualLogSource _manualLogSource = manualLogSource;
/// <summary>
/// Logs a message with specified parameters.
/// </summary>
/// <param name="info">The message to log.</param>
/// <param name="tag">The log tag/category.</param>
/// <param name="logConsole">Whether to output to console.</param>
/// <param name="color">The console color for the message.</param>
/// <param name="hostOnly">Whether to log only when the client is host.</param>
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 { }
}
/// <summary>
/// Logs a header message with visual formatting.
/// </summary>
/// <param name="info">The header text.</param>
/// <param name="tag">The log tag/category.</param>
/// <param name="hostOnly">Whether to log only when the client is host.</param>
/// <param name="logConsole">Whether to output to console.</param>
internal void LogHeader(string info, string tag = "LogHeader", bool hostOnly = false, bool logConsole = true) => Log($" >-------------- {info} --------------<", tag, hostOnly: hostOnly, logConsole: logConsole);
/// <summary>
/// Logs cheat detection messages with green console color.
/// </summary>
/// <param name="info">The cheat detection message.</param>
/// <param name="tag">The log tag/category.</param>
/// <param name="hostOnly">Whether to log only when the client is host.</param>
/// <param name="logConsole">Whether to output to console.</param>
internal void LogCheat(string info, string tag = "AntiCheat", bool hostOnly = false, bool logConsole = true) => Log(info, tag, color: ConsoleColor.Green, hostOnly: hostOnly, logConsole: logConsole);
/// <summary>
/// Logs error messages with red console color.
/// </summary>
/// <param name="info">The error message.</param>
/// <param name="tag">The log tag/category.</param>
/// <param name="hostOnly">Whether to log only when the client is host.</param>
/// <param name="logConsole">Whether to output to console.</param>
internal void Error(string info, string tag = "Error", bool hostOnly = false, bool logConsole = true) => Log(info, tag, color: ConsoleColor.Red, hostOnly: hostOnly, logConsole: logConsole);
/// <summary>
/// Logs exception details with red console color.
/// </summary>
/// <param name="ex">The exception to log.</param>
/// <param name="tag">The log tag/category.</param>
/// <param name="hostOnly">Whether to log only when the client is host.</param>
/// <param name="logConsole">Whether to output to console.</param>
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);
/// <summary>
/// Logs warning messages with yellow console color.
/// </summary>
/// <param name="info">The warning message.</param>
/// <param name="tag">The log tag/category.</param>
/// <param name="hostOnly">Whether to log only when the client is host.</param>
/// <param name="logConsole">Whether to output to console.</param>
internal void Warning(string info, string tag = "Warning", bool hostOnly = false, bool logConsole = true) => Log(info, tag, color: ConsoleColor.Yellow, hostOnly: hostOnly, logConsole: logConsole);
/// <summary>
/// Logs a test message for debugging purposes.
/// </summary>
internal void Test()
{
Log("------------------> TEST <------------------", "TEST");
InGame("TEST");
}
/// <summary>
/// Logs a message in-game via the disconnect message notifier.
/// </summary>
/// <param name="info">The message to display in-game.</param>
/// <param name="hostOnly">Whether to log only when the client is host.</param>
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);
}
/// <summary>
/// Logs a private message with encryption for sensitive data.
/// </summary>
/// <param name="info">The sensitive message to log.</param>
/// <param name="tag">The log tag/category.</param>
/// <param name="hostOnly">Whether to log only when the client is host.</param>
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 { }
}
/// <summary>
/// Decrypts all encrypted sections within a log file text.
/// </summary>
/// <param name="logText">The raw log file content containing encrypted sections marked with prefix and postfix characters.</param>
/// <returns>The log text with all encrypted sections decrypted and markers removed.</returns>
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;
}
}
/// <summary>
/// Custom log listener for BepInEx that forwards logs to the BetterAmongUs logging system.
/// </summary>
internal class CustomLogListener(BAULogger bauLogger) : ILogListener
{
private readonly BAULogger _bauLogger = bauLogger;
/// <summary>
/// Gets or sets the log levels to filter.
/// </summary>
public LogLevel LogLevelFilter { get; set; } = LogLevel.Info | LogLevel.Warning | LogLevel.Error;
/// <summary>
/// Handles log events from BepInEx.
/// </summary>
/// <param name="sender">The event sender.</param>
/// <param name="eventArgs">The log event arguments.</param>
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);
}
}
/// <summary>
/// Disposes the log listener.
/// </summary>
public void Dispose() { }
/// <summary>
/// Flushes the log listener.
/// </summary>
public void Flush() { }
}
+77 -3
View File
@@ -1,5 +1,5 @@
using System.Security.Cryptography;
using System.Text;
using UnityEngine;
namespace BetterAmongUs.Modules;
@@ -8,16 +8,82 @@ namespace BetterAmongUs.Modules;
/// </summary>
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 = [];
/// <summary>
/// Initializes the encryption system by loading or generating AES keys.
/// </summary>
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();
}
}
/// <summary>
/// Generates a new 256-bit AES key and 128-bit initialization vector (IV),
/// then saves them to PlayerPrefs as Base64 strings.
/// </summary>
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();
}
/// <summary>
/// Encrypts a plain text string using AES encryption.
/// </summary>
/// <param name="input">The plain text string to encrypt.</param>
/// <returns>A base64-encoded string containing the encrypted data.</returns>
/// <exception cref="InvalidOperationException">Thrown if encryption is not initialized.</exception>
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
/// </summary>
/// <param name="input">The base64-encoded encrypted string.</param>
/// <returns>The decrypted plain text string.</returns>
/// <exception cref="InvalidOperationException">Thrown if encryption is not initialized.</exception>
/// <exception cref="FormatException">Thrown if the input is not valid Base64.</exception>
/// <exception cref="CryptographicException">Thrown if decryption fails due to invalid key or corrupted data.</exception>
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;
-240
View File
@@ -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;
/// <summary>
/// Provides logging utilities for BetterAmongUs with various log levels and destinations.
/// </summary>
internal static class Logger_
{
/// <summary>
/// Logs a message with specified parameters.
/// </summary>
/// <param name="info">The message to log.</param>
/// <param name="tag">The log tag/category.</param>
/// <param name="logConsole">Whether to output to console.</param>
/// <param name="color">The console color for the message.</param>
/// <param name="hostOnly">Whether to log only when the client is host.</param>
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 { }
}
/// <summary>
/// Logs method calls with detailed information about the caller.
/// </summary>
/// <param name="info">Additional information about the method call.</param>
/// <param name="runtimeType">The type containing the method (optional).</param>
/// <param name="hostOnly">Whether to log only when the client is host.</param>
/// <param name="callerFilePath">Automatically populated: path of the file containing the caller.</param>
/// <param name="callerLineNumber">Automatically populated: line number of the caller.</param>
/// <param name="callerMemberName">Automatically populated: name of the calling member.</param>
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);
}
/// <summary>
/// Logs private method calls with detailed information, using encrypted storage.
/// </summary>
/// <param name="info">Additional information about the method call.</param>
/// <param name="runtimeType">The type containing the method (optional).</param>
/// <param name="hostOnly">Whether to log only when the client is host.</param>
/// <param name="callerFilePath">Automatically populated: path of the file containing the caller.</param>
/// <param name="callerLineNumber">Automatically populated: line number of the caller.</param>
/// <param name="callerMemberName">Automatically populated: name of the calling member.</param>
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);
}
/// <summary>
/// Logs a header message with visual formatting.
/// </summary>
/// <param name="info">The header text.</param>
/// <param name="tag">The log tag/category.</param>
/// <param name="hostOnly">Whether to log only when the client is host.</param>
/// <param name="logConsole">Whether to output to console.</param>
internal static void LogHeader(string info, string tag = "LogHeader", bool hostOnly = false, bool logConsole = true) => Log($" >-------------- {info} --------------<", tag, hostOnly: hostOnly, logConsole: logConsole);
/// <summary>
/// Logs cheat detection messages with green console color.
/// </summary>
/// <param name="info">The cheat detection message.</param>
/// <param name="tag">The log tag/category.</param>
/// <param name="hostOnly">Whether to log only when the client is host.</param>
/// <param name="logConsole">Whether to output to console.</param>
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);
/// <summary>
/// Logs error messages with red console color.
/// </summary>
/// <param name="info">The error message.</param>
/// <param name="tag">The log tag/category.</param>
/// <param name="hostOnly">Whether to log only when the client is host.</param>
/// <param name="logConsole">Whether to output to console.</param>
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);
/// <summary>
/// Logs exception details with red console color.
/// </summary>
/// <param name="ex">The exception to log.</param>
/// <param name="tag">The log tag/category.</param>
/// <param name="hostOnly">Whether to log only when the client is host.</param>
/// <param name="logConsole">Whether to output to console.</param>
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);
/// <summary>
/// Logs warning messages with yellow console color.
/// </summary>
/// <param name="info">The warning message.</param>
/// <param name="tag">The log tag/category.</param>
/// <param name="hostOnly">Whether to log only when the client is host.</param>
/// <param name="logConsole">Whether to output to console.</param>
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);
/// <summary>
/// Logs a test message for debugging purposes.
/// </summary>
internal static void Test()
{
Log("------------------> TEST <------------------", "TEST");
InGame("TEST");
}
/// <summary>
/// Logs a message in-game via the disconnect message notifier.
/// </summary>
/// <param name="info">The message to display in-game.</param>
/// <param name="hostOnly">Whether to log only when the client is host.</param>
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);
}
/// <summary>
/// Logs a private message with encryption for sensitive data.
/// </summary>
/// <param name="info">The sensitive message to log.</param>
/// <param name="tag">The log tag/category.</param>
/// <param name="hostOnly">Whether to log only when the client is host.</param>
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 { }
}
}
/// <summary>
/// Custom log listener for BepInEx that forwards logs to the BetterAmongUs logging system.
/// </summary>
internal class CustomLogListener : ILogListener
{
/// <summary>
/// Gets or sets the log levels to filter.
/// </summary>
public LogLevel LogLevelFilter { get; set; } = LogLevel.Info | LogLevel.Warning | LogLevel.Error;
/// <summary>
/// Handles log events from BepInEx.
/// </summary>
/// <param name="sender">The event sender.</param>
/// <param name="eventArgs">The log event arguments.</param>
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);
}
}
/// <summary>
/// Disposes the log listener.
/// </summary>
public void Dispose() { }
/// <summary>
/// Flushes the log listener.
/// </summary>
public void Flush() { }
}
+1 -1
View File
@@ -238,7 +238,7 @@ public sealed class OptionPlayerItem : OptionItem<int>
{
if (Value != -1)
{
var player = Utils.PlayerFromPlayerId(Value);
var player = Utils.PlayerFromPlayerId((byte)Value);
if (player != null)
return $"{player.GetPlayerNameAndColor()}";
else
+1 -1
View File
@@ -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;
}
}
+15 -52
View File
@@ -9,25 +9,14 @@ namespace BetterAmongUs.Modules;
internal static class TextFileHandler
{
/// <summary>
/// Compares strings against filter patterns in a file using wildcard matching.
/// Compares a string against regex patterns in a file.
/// </summary>
/// <param name="filePath">Path to the filter file.</param>
/// <param name="strings">Array of strings to check against filters.</param>
/// <returns>True if any string matches a filter pattern, false otherwise.</returns>
internal static bool CompareStringFilters(string filePath, string[] strings)
/// <param name="filePath">Path to the file containing regex patterns.</param>
/// <param name="inputString">String to match against.</param>
/// <returns>True if the string matches a regex pattern, false otherwise.</returns>
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));
}
/// <summary>
@@ -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);
}
/// <summary>
@@ -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 [];
}
/// <summary>
/// Checks if a text matches a filter pattern with wildcard support.
/// </summary>
/// <param name="filter">The filter pattern (supports ** wildcards).</param>
/// <param name="text">The text to check.</param>
/// <returns>True if the text matches the filter pattern, false otherwise.</returns>
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);
}
/// <summary>
/// Parses a YAML-like string into a dictionary of key-value pairs.
/// </summary>
+9 -9
View File
@@ -22,9 +22,9 @@ internal static class Translator
/// </summary>
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");
}
/// <summary>
@@ -34,14 +34,14 @@ internal static class Translator
{
try
{
var assembly = ModInfo.Assembly;
var assembly = BAUPlugin.ModInfo.Assembly;
var jsonFileNames = GetJsonResourceNames(assembly);
TranslateMaps = [];
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;
}
}
+3 -3
View File
@@ -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;
}
@@ -84,8 +84,8 @@ internal sealed class BetterPingTracker : MonoBehaviour
sb.AppendFormat("{0}: <b>{1}</b>\n", TranslationStrings.Timer.LocalizedString.ToUpper(), $"<{timeColor}>{lobbyTimerDisplay}</color>");
}
sb.Append($"<color=#00dbdb><size=75%>BetterAmongUs {BAUPlugin.GetVersionText(true)}</size></color>\n");
sb.Append($"<color=#8A8A8A>{ModInfo.GITHUB}</color>\n".Size(52f));
sb.Append($"<color=#00dbdb><size=75%>BetterAmongUs {BAUPlugin.ModInfo.VERSION_STRING}</size></color>\n");
sb.Append($"<color=#8A8A8A>{BAUPlugin.ModInfo.GITHUB}</color>\n".Size(52f));
if (BAUConfigs.ShowFPS.Value)
{
@@ -29,7 +29,7 @@ internal sealed class ExtendedPlayerInfo : MonoBehaviour, IMonoExtension<Network
}
catch (Exception ex)
{
Logger_.Log("Handshake disabled: " + ex.Message);
BAUPlugin.Logger.Log("Handshake disabled: " + ex.Message);
HandshakeHandler = null;
}
}
@@ -65,7 +65,7 @@ internal sealed class ExtendedPlayerInfo : MonoBehaviour, IMonoExtension<Network
TranslationStrings.AntiCheat_UnauthorizedAction.LocalizedString
);
Logger_.LogCheat($"{BaseMono.Object.ExtendedData().RealName} {AntiCheatInfo.RPCSentPS} Sent.");
BAUPlugin.Logger.LogCheat($"{BaseMono.Object.ExtendedData().RealName} {AntiCheatInfo.RPCSentPS} Sent.");
}
}
@@ -93,7 +93,7 @@ internal sealed class ExtendedPlayerInfo : MonoBehaviour, IMonoExtension<Network
{
try
{
if (reader.ReadString() == ModInfo.Constants.BAU_MODDED_PROTOCOL_FLAG)
if (reader.ReadString() == BAUPlugin.Constants.BAU_MODDED_PROTOCOL_FLAG)
{
int flagCount = reader.ReadPackedInt32();
for (int i = 0; i < flagCount; i++)
+9 -1
View File
@@ -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;
+9 -1
View File
@@ -217,7 +217,7 @@ internal class PlayerInfoDisplay : MonoBehaviour
}
else
{
var targetData = Utils.PlayerDataFromPlayerId(_player.shapeshiftTargetPlayerId);
var targetData = Utils.PlayerDataFromPlayerId((byte)_player.shapeshiftTargetPlayerId);
if (targetData == null)
return;
@@ -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);
+15 -68
View File
@@ -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
{
/// <summary>
/// Gets or sets the download link for the updated DLL file.
/// </summary>
[JsonPropertyName("valid")]
public bool Valid { get; set; } = false;
/// <summary>
/// Gets or sets the download link for the updated DLL file.
/// </summary>
@@ -24,86 +30,27 @@ internal sealed class BAUUpdateData
[JsonPropertyName("version")]
public string Version { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the release type as an integer value.
/// </summary>
[JsonPropertyName("releaseType")]
public int ReleaseType { get; set; }
/// <summary>
/// Gets or sets whether this update is a hotfix.
/// </summary>
[JsonPropertyName("isHotfix")]
public bool IsHotfix { get; set; }
/// <summary>
/// Gets or sets the hotfix number if this is a hotfix update.
/// </summary>
[JsonPropertyName("hotfixNumber")]
public int HotfixNumber { get; set; }
/// <summary>
/// Gets or sets the beta number if this is a beta release.
/// </summary>
[JsonPropertyName("betaNumber")]
public int BetaNumber { get; set; }
/// <summary>
/// Determines if this update is newer than the currently installed version.
/// </summary>
/// <returns>True if the update is newer, false otherwise.</returns>
/// <remarks>
/// Compares version numbers, release types, and specific build numbers
/// to determine if an update should be applied.
/// </remarks>
internal bool IsNewUpdate()
{
try
{
var updateVersion = new Version(Version);
var modVersion = new Version(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 = 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(ModInfo.BETA_NUM);
}
// Release version - check hotfixes
if (IsHotfix && !ModInfo.IS_HOTFIX)
return true;
if (IsHotfix && ModInfo.IS_HOTFIX)
return HotfixNumber > int.Parse(ModInfo.HOTFIX_NUM);
// Same version, same type, no newer hotfix
return false;
return updateVersion.ComparePrecedenceTo(modVersion) > 0;
}
catch (Exception ex)
{
Logger_.Error($"Update check failed: {ex.Message}");
BAUPlugin.Logger.Error($"Update check failed: {ex.Message}");
return false;
}
}
@@ -132,7 +79,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");
@@ -152,6 +99,6 @@ internal sealed class BAUUpdateData
/// <returns>A formatted string containing version information.</returns>
public override string ToString()
{
return $"{Version}:{ReleaseType}:{BetaNumber}:{HotfixNumber}";
return $"{SemVersion.Parse(Version)}";
}
}
+1 -1
View File
@@ -80,7 +80,7 @@ internal sealed class NewsData()
}
catch (Exception ex)
{
Logger_.Error($"Failed to manually deserialize YAML: {ex.Message}");
BAUPlugin.Logger.Error($"Failed to manually deserialize YAML: {ex.Message}");
return null;
}
}
+3 -3
View File
@@ -64,7 +64,7 @@ internal static class GitHubFile
}
else
{
Logger_.Error($"Error downloading file from URL '{url}': {www.error} (Response Code: {(int)www.responseCode})");
BAUPlugin.Logger.Error($"Error downloading file from URL '{url}': {www.error} (Response Code: {(int)www.responseCode})");
if (showProgress)
{
CustomLoadingBarManager.SetLoadingPercent(100f, "Download Failed!");
@@ -77,7 +77,7 @@ internal static class GitHubFile
byte[] bytes = www.downloadHandler.GetNativeData().ToArray();
File.WriteAllBytes(localFilePath, bytes);
Logger_.Log($"Saved file: {localFilePath}");
BAUPlugin.Logger.Log($"Saved file: {localFilePath}");
callback?.Invoke(localFilePath);
}
@@ -98,7 +98,7 @@ internal static class GitHubFile
if (www.result != UnityWebRequest.Result.Success)
{
Logger_.Error($"Error downloading {url}: {www.error}");
BAUPlugin.Logger.Error($"Error downloading {url}: {www.error}");
yield break;
}
+1 -1
View File
@@ -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<BAUUpdateLoader>();
yield return updateLoader.CoFetchUpdateData();
+2 -6
View File
@@ -25,10 +25,6 @@ internal sealed class BAUUpdateLoader : MonoBehaviour
/// Coroutine to fetch update data from the remote repository.
/// </summary>
/// <returns>IEnumerator for coroutine execution.</returns>
/// <remarks>
/// If no internet connection is detected, it retries several times before giving up.
/// After successfully loading update data, it initializes the UpdateManager.
/// </remarks>
[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;
});
@@ -65,7 +61,7 @@ internal sealed class BAUUpdateLoader : MonoBehaviour
if (response != null)
{
UpdateInfo = response;
Logger_.Log($"Loaded update info");
BAUPlugin.Logger.Log($"Loaded update info");
}
BAUUpdateManager.Init();
+5 -5
View File
@@ -53,7 +53,7 @@ internal sealed class NewsLoader : MonoBehaviour
if (response == null || !response.ContainsKey("News"))
{
Logger_.Error("manifest.json deserialization failed or no 'News' key found.");
BAUPlugin.Logger.Error("manifest.json deserialization failed or no 'News' key found.");
yield break;
}
@@ -64,7 +64,7 @@ internal sealed class NewsLoader : MonoBehaviour
yield return CoLoadNewsTest();
Logger_.Log($"Loaded {ModNews.NewsDataToProcess.Count} news files");
BAUPlugin.Logger.Log($"Loaded {ModNews.NewsDataToProcess.Count} news files");
Destroy(this);
}
@@ -97,7 +97,7 @@ internal sealed class NewsLoader : MonoBehaviour
}
catch (Exception ex)
{
Logger_.Error($"Failed to deserialize yaml for '{fileName}': {ex.Message}");
BAUPlugin.Logger.Error($"Failed to deserialize yaml for '{fileName}': {ex.Message}");
yield break;
}
}
@@ -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)
{
@@ -124,7 +124,7 @@ internal sealed class NewsLoader : MonoBehaviour
}
catch (Exception ex)
{
Logger_.Error($"Failed to deserialize yaml for '{yamlDirectory}': {ex.Message}");
BAUPlugin.Logger.Error($"Failed to deserialize yaml for '{yamlDirectory}': {ex.Message}");
yield break;
}
}
+2 -2
View File
@@ -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;
}
+4 -4
View File
@@ -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());
@@ -56,7 +56,7 @@ internal static class ClientPatch
// Show warning popup for newer Among Us version
Utils.ShowPopUp($"<size=200%>-= <color=#ff2200><b>Warning</b></color> =-</size>\n\n" +
$"<size=125%><color=#0dff00>Better Among Us {BAUPlugin.GetVersionText()}</color>\nsupports <color=#4f92ff>Among Us {verText}</color>,\n" +
$"<size=125%><color=#0dff00>Better Among Us {BAUPlugin.ModInfo.VERSION_STRING}</color>\nsupports <color=#4f92ff>Among Us {verText}</color>,\n" +
$"<color=#4f92ff>Among Us <b>{BAUPlugin.AppVersion}</b></color> is above the supported versions!\n" +
$"<color=#ae1700>You may encounter minor to game breaking bugs.</color></size>");
}
@@ -71,7 +71,7 @@ internal static class ClientPatch
// Show warning popup for older Among Us version
Utils.ShowPopUp($"<size=200%>-= <color=#ff2200><b>Warning</b></color> =-</size>\n\n" +
$"<size=125%><color=#0dff00>Better Among Us {BAUPlugin.GetVersionText()}</color>\nsupports <color=#4f92ff>Among Us {verText}</color>,\n" +
$"<size=125%><color=#0dff00>Better Among Us {BAUPlugin.ModInfo.VERSION_STRING}</color>\nsupports <color=#4f92ff>Among Us {verText}</color>,\n" +
$"<color=#4f92ff>Among Us <b>{BAUPlugin.AppVersion}</b></color> is below the supported versions!\n" +
$"<color=#ae1700>You may encounter minor to game breaking bugs.</color></size>");
}
@@ -86,7 +86,7 @@ internal static class ClientPatch
// Hide custom loading bar when exiting game
CustomLoadingBarManager.ToggleLoadingBar(false);
Logger_.Log($"Client has left game for: {Enum.GetName(reason)}", "AmongUsClientPatch");
BAUPlugin.Logger.Log($"Client has left game for: {Enum.GetName(reason)}", "AmongUsClientPatch");
}
[HarmonyPatch(typeof(AmongUsClient), nameof(AmongUsClient.OnGameEnd))]
+2 -2
View File
@@ -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{ModInfo.PLUGIN_VERSION} - Hide & Seek";
details = $"BAU v{BAUPlugin.ModInfo.VERSION_STRING} - Hide & Seek";
}
}
catch
@@ -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,12 +56,14 @@ 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)
if (!ModInfo.Starlight)
if (!BAUPlugin.ModInfo.Starlight)
{
ClientOptionItem.CreateButton(TranslationStrings.BetterOption_SaveData, -1, __instance, OpenSaveData, () =>
{
+1 -1
View File
@@ -13,6 +13,6 @@ internal static class VersionShowerPatch
{
string mark = TranslationStrings.BAUMark.LocalizedString;
string bau = TranslationStrings.BAU.LocalizedString;
__instance.text.text = $"<color=#0dff00>{mark}{bau}{mark} {BAUPlugin.GetVersionText()}</color> <color=#ababab>~</color> {Utils.GetPlatformName(BAUPlugin.PlatformData.Platform)} v{BAUPlugin.AmongUsVersion} ({BAUPlugin.AppVersion})";
__instance.text.text = $"<color=#0dff00>{mark}{bau}{mark} {BAUPlugin.ModInfo.VERSION_STRING}</color> <color=#ababab>~</color> {Utils.GetPlatformName(BAUPlugin.PlatformData.Platform)} v{BAUPlugin.AmongUsVersion} ({BAUPlugin.AppVersion})";
}
}
@@ -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");
}
}
@@ -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)
{
@@ -51,7 +57,7 @@ internal static class PlatformSpoofPatch
TranslationStrings.AntiCheat_Reason_PlatformSpoofer.LocalizedString,
TranslationStrings.AntiCheat_HasBeenDetectedWithCheat.LocalizedString
);
Logger_.LogCheat($"{player.ExtendedData().RealName} {TranslationStrings.AntiCheat_Reason_PlatformSpoofer}: {__instance.XboxPlatformId}");
BAUPlugin.Logger.LogCheat($"{player.ExtendedData().RealName} {TranslationStrings.AntiCheat_Reason_PlatformSpoofer}: {__instance.XboxPlatformId}");
}
}
@@ -66,7 +72,7 @@ internal static class PlatformSpoofPatch
TranslationStrings.AntiCheat_Reason_PlatformSpoofer.LocalizedString,
TranslationStrings.AntiCheat_HasBeenDetectedWithCheat.LocalizedString
);
Logger_.LogCheat($"{player.ExtendedData().RealName} {TranslationStrings.AntiCheat_Reason_PlatformSpoofer}: {__instance.PsnPlatformId}");
BAUPlugin.Logger.LogCheat($"{player.ExtendedData().RealName} {TranslationStrings.AntiCheat_Reason_PlatformSpoofer}: {__instance.PsnPlatformId}");
}
}
@@ -124,7 +124,7 @@ internal static class VoteBanSystemPatch
maxVotes = votes.Length; // Total possible votes
}
Logger_.InGame(
BAUPlugin.Logger.InGame(
$"{src.Character?.GetPlayerNameAndColor() ?? src.PlayerName} " +
$"voted to kick {client.Character?.GetPlayerNameAndColor() ?? client.PlayerName} " +
$"<#6F6F6F>(</color><#FFFFFF>{currentVotes}</color><#6F6F6F>/</color><#FFFFFF>{maxVotes}</color><#6F6F6F>)</color>"
+1 -1
View File
@@ -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");
}
}
@@ -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<TextMeshPro>()
: 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<TextMeshPro>();
}
if (tmpro == null)
{
tmpro = CreateNewTextMeshPro(child);
}
if (tmpro != null)
{
tmpro.font = container.capacity?.font;
tmpro.fontSize = 3f;
tmpro.text = FormatGameInfoText(container.gameListing);
}
}
}
@@ -13,7 +13,7 @@ namespace BetterAmongUs.Patches.Gameplay.Managers;
[HarmonyPatch]
internal static class HudManagerPatch
{
internal static string WelcomeMessage => $"<b><color=#00b530><size=125%><align=\"center\">{TranslationStrings.WelcomeMsg_WelcomeToBAU.Format(TranslationStrings.BetterAmongUs)}\n{BAUPlugin.GetVersionText()}</size>\n" +
internal static string WelcomeMessage => $"<b><color=#00b530><size=125%><align=\"center\">{TranslationStrings.WelcomeMsg_WelcomeToBAU.Format(TranslationStrings.BetterAmongUs)}\n{BAUPlugin.ModInfo.VERSION_STRING}</size>\n" +
$"{TranslationStrings.WelcomeMsg_ThanksForDownloading}</align></color></b>\n<size=120%> </size>\n" +
TranslationStrings.WelcomeMsg_BAUDescription1.Format(TranslationStrings.BAU, TranslationStrings.BetterOption_AntiCheat);
@@ -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;
}
@@ -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;
}
@@ -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))
@@ -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)
{
@@ -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");
}
}
@@ -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))]
@@ -74,7 +74,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_BanNameListMessage.LocalizedString, bypassDataCheck: true);
}
@@ -152,7 +153,7 @@ internal static class PlayerJoinAndLeftPatch
{
var ReasonText = $"<color=#ff0>{playerData.ExtendedData().RealName}</color> {forceReasonText}";
Logger_.Log(ReasonText);
BAUPlugin.Logger.Log(ReasonText);
HudManager.Instance.Notifier.AddDisconnectMessage(ReasonText);
}
@@ -189,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);
+20 -30
View File
@@ -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;
@@ -218,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");
}
BAUPlugin.Logger.LogPrivate($"{sourcePlayer.Data.PlayerName} -> {chatText}", "ChatLog");
}
[HarmonyPatch(typeof(ChatController), nameof(ChatController.SetChatBubbleName))]
@@ -294,9 +286,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 +322,29 @@ internal static class ChatPatch
chat.quickChatButton.transform.Find("QuickChatIcon").GetComponent<SpriteRenderer>().color = new Color(1f, 1f, 1f, 1f);
chat.openKeyboardButton.transform.Find("OpenKeyboardIcon").GetComponent<SpriteRenderer>().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<ChatBubble>()))
{
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<TextMeshPro>(true).color = Color.white;
chatBubble.transform.Find("Background").GetComponentInChildren<SpriteRenderer>(true).color = new Color(0.15f, 0.15f, 0.15f, 1f);
var mark = chatBubble.transform.Find("PoolablePlayer/xMark");
if (mark != null && mark.GetComponentInChildren<SpriteRenderer>(true).enabled)
{
chatBubble.transform.Find("Background").GetComponentInChildren<SpriteRenderer>(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<TextMeshPro>(true).color = Color.black;
chatBubble.transform.Find("Background").GetComponentInChildren<SpriteRenderer>(true).color = Color.white;
var mark = chatBubble.transform.Find("PoolablePlayer/xMark");
if (mark != null && mark.GetComponentInChildren<SpriteRenderer>(true).enabled)
{
chatBubble.transform.Find("Background").GetComponentInChildren<SpriteRenderer>(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;
@@ -369,8 +359,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 +369,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 +377,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);
}
}
+5 -5
View File
@@ -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<size=58%>{playerList}</size>";
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;
+2 -2
View File
@@ -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");
}
}
@@ -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<IHudOverrideTask>(PlayerControl.LocalPlayer) ? Palette.DisabledGrey : new Color(0.2f, 0.5f, 0f, 1f));
{
if (!BAUConfigs.BetterMinimapColors.Value)
{
return;
}
__instance.BackgroundColor.SetColor(PlayerTask.PlayerHasTaskOfType<IHudOverrideTask>(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<IHudOverrideTask>(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
/// <summary>
/// Clears all map icons to be regenerated
/// </summary>
internal static void ClearMapIcons()
internal static void ForceCloseMiniMap()
{
if (_icons != null)
{
@@ -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);
@@ -16,19 +16,19 @@ 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;
internal static OptionIntItem? KickLevelBelow;
internal static OptionIntItem? KickLevelBelowMinimumPlayers;
internal static OptionCheckboxItem? DetectCheatClients;
internal static OptionCheckboxItem? DetectInvalidRpcs;
internal static OptionCheckboxItem? RpcRateLimiting;
internal static OptionIntItem? RpcRateLimit;
internal static OptionStringItem? RoleRandomizer;
internal static OptionCheckboxItem? CancelInvalidSabotage;
internal static OptionCheckboxItem? CensorDetectionReason;
}
@@ -75,8 +75,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
@@ -86,19 +87,13 @@ 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);
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();
}
+2 -3
View File
@@ -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)
+21 -10
View File
@@ -10,21 +10,32 @@ namespace BetterAmongUs.Patches.Unity;
[HarmonyPatch]
internal static class UnityWebRequestPatch
{
// Build mod version header string
/// <summary>
/// Builds a mod version header string for network transmission.
/// Format: "major;minor;patch;metadata1/metadata2/metadata3"
/// </summary>
public static string GetHeader()
{
var stringBuilder = new StringBuilder();
var version = BAUPlugin.ModInfo.SemVersion;
// Format: "Version;BuildType;IsHotfix/HotfixNum/BetaNum"
stringBuilder.Append(ModInfo.PLUGIN_VERSION);
stringBuilder.Append(version.Major);
stringBuilder.Append(';');
stringBuilder.Append(Enum.GetName(ModInfo.ReleaseBuildType));
stringBuilder.Append(version.Minor);
stringBuilder.Append(';');
stringBuilder.Append(ModInfo.IS_HOTFIX);
stringBuilder.Append('/');
stringBuilder.Append(ModInfo.HOTFIX_NUM);
stringBuilder.Append('/');
stringBuilder.Append(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();
}
@@ -68,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");
}
}));
}
+6 -7
View File
@@ -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",
@@ -73,9 +75,7 @@
"BetterSetting.SetTo": "Set To:",
"BetterSetting.MainHeader.System": "<color=#07B400>System Settings</color>",
"BetterSetting.MainHeader.AntiCheat": "<color=#4f92ff>Anti-Cheat Settings</color>",
"BetterSetting.MainHeader.RoleAlgorithm": "<color=#4f92ff>Role Algorithm Settings</color>",
"BetterSetting.MainHeader.Gameplay": "<color=#d7d700>Gameplay Settings</color>",
"BetterSetting.MainHeader.HideNSeek": "<color=#d7d700>Hide & Seek Settings</color>",
"BetterSetting.TextHeader.HostOnly": "<color=#4f92ff>Host Only</color>",
"BetterSetting.TextHeader.Detections": "<color=#4f92ff>Detections</color>",
@@ -88,20 +88,19 @@
"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.ShowRoleForClients": "Show role in name for clients",
"BetterSetting.Setting.UseBanChatList": "Use ban chat list",
"BetterSetting.Setting.UseBanChatListOnlyLobby": "Only in lobby",
"BetterSetting.Setting.UseBanChatListBan": "Ban",
"BetterSetting.Setting.CensorDetectionReason": "Censor detection reason",
"BetterSetting.Setting.DetectedLevel": "Detected player levels",
"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",
"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",
+1 -1
View File
@@ -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;
}
/// <summary>
+3 -2
View File
@@ -80,7 +80,7 @@ internal static class RoleUtils
return color;
}
return string.Empty;
return "#ffffff";
}
/// <summary>
@@ -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" },
+14 -12
View File
@@ -1,4 +1,5 @@
using BepInEx.Unity.IL2CPP.Utils;
using BetterAmongUs.Enums;
using BetterAmongUs.Generated;
using BetterAmongUs.Modules;
using BetterAmongUs.Patches.Gameplay.UI.Chat;
@@ -77,7 +78,7 @@ internal static class Utils
/// </summary>
/// <param name="clientId">The client ID to look up.</param>
/// <returns>The ClientData if found, null otherwise.</returns>
internal static ClientData? ClientFromClientId(int clientId) =>
internal static ClientData? ClientFromClientId(ClientId clientId) =>
AmongUsClient.Instance.allClients.FirstOrDefaultIl2Cpp(cd => cd.Id == clientId);
/// <summary>
@@ -85,7 +86,7 @@ internal static class Utils
/// </summary>
/// <param name="playerId">The player ID to look up.</param>
/// <returns>The NetworkedPlayerInfo if found, null otherwise.</returns>
internal static NetworkedPlayerInfo? PlayerDataFromPlayerId(int playerId) =>
internal static NetworkedPlayerInfo? PlayerDataFromPlayerId(PlayerId playerId) =>
GameData.Instance.AllPlayers.FirstOrDefaultIl2Cpp(data => data.PlayerId == playerId);
/// <summary>
@@ -93,7 +94,7 @@ internal static class Utils
/// </summary>
/// <param name="clientId">The client ID to look up.</param>
/// <returns>The NetworkedPlayerInfo if found, null otherwise.</returns>
internal static NetworkedPlayerInfo? PlayerDataFromClientId(int clientId) =>
internal static NetworkedPlayerInfo? PlayerDataFromClientId(ClientId clientId) =>
GameData.Instance.AllPlayers.FirstOrDefaultIl2Cpp(data => data.ClientId == clientId);
/// <summary>
@@ -109,7 +110,7 @@ internal static class Utils
/// </summary>
/// <param name="playerId">The player ID to look up.</param>
/// <returns>The PlayerControl if found, null otherwise.</returns>
internal static PlayerControl? PlayerFromPlayerId(int playerId) =>
internal static PlayerControl? PlayerFromPlayerId(PlayerId playerId) =>
BAUPlugin.AllPlayerControls.FirstOrDefault(player => player.PlayerId == playerId);
/// <summary>
@@ -117,7 +118,7 @@ internal static class Utils
/// </summary>
/// <param name="clientId">The client ID to look up.</param>
/// <returns>The PlayerControl if found, null otherwise.</returns>
internal static PlayerControl? PlayerFromClientId(int clientId) =>
internal static PlayerControl? PlayerFromClientId(ClientId clientId) =>
BAUPlugin.AllPlayerControls.FirstOrDefault(player => player.GetClientId() == clientId);
/// <summary>
@@ -125,7 +126,7 @@ internal static class Utils
/// </summary>
/// <param name="netId">The network ID to look up.</param>
/// <returns>The PlayerControl if found, null otherwise.</returns>
internal static PlayerControl? PlayerFromNetId(uint netId) =>
internal static PlayerControl? PlayerFromNetId(NetId netId) =>
BAUPlugin.AllPlayerControls.FirstOrDefault(player => player.NetId == netId);
// Chat functionality
@@ -430,7 +431,7 @@ internal static class Utils
}
catch (Exception ex)
{
Logger_.Error(ex);
BAUPlugin.Logger.Error(ex);
return null;
}
}
@@ -444,7 +445,7 @@ internal static class Utils
{
try
{
var stream = ModInfo.Assembly.GetManifestResourceStream(path);
var stream = BAUPlugin.ModInfo.Assembly.GetManifestResourceStream(path);
if (stream == null)
return null;
@@ -460,7 +461,7 @@ internal static class Utils
}
catch (Exception ex)
{
Logger_.Error(ex);
BAUPlugin.Logger.Error(ex);
return null;
}
}
@@ -474,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;
}
@@ -496,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;
}
}
@@ -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)
};
+20 -25
View File
@@ -323,6 +323,16 @@ public static class TranslationStrings
/// </summary>
public static readonly TranslationString BetterOption_MinimapIcons = new("BetterOption.MinimapIcons");
/// <summary>
/// Base Translation: Better Minimap Colors
/// </summary>
public static readonly TranslationString BetterOption_BetterMinimapColors = new("BetterOption.BetterMinimapColors");
/// <summary>
/// Base Translation: Better Colorblind Text
/// </summary>
public static readonly TranslationString BetterOption_BetterColorblindText = new("BetterOption.BetterColorblindText");
/// <summary>
/// Base Translation: Compress Setting Files
/// </summary>
@@ -363,21 +373,11 @@ public static class TranslationStrings
/// </summary>
public static readonly TranslationString BetterSetting_MainHeader_AntiCheat = new("BetterSetting.MainHeader.AntiCheat");
/// <summary>
/// Base Translation: &lt;color=#4f92ff&gt;Role Algorithm Settings&lt;/color&gt;
/// </summary>
public static readonly TranslationString BetterSetting_MainHeader_RoleAlgorithm = new("BetterSetting.MainHeader.RoleAlgorithm");
/// <summary>
/// Base Translation: &lt;color=#d7d700&gt;Gameplay Settings&lt;/color&gt;
/// </summary>
public static readonly TranslationString BetterSetting_MainHeader_Gameplay = new("BetterSetting.MainHeader.Gameplay");
/// <summary>
/// Base Translation: &lt;color=#d7d700&gt;Hide &amp; Seek Settings&lt;/color&gt;
/// </summary>
public static readonly TranslationString BetterSetting_MainHeader_HideNSeek = new("BetterSetting.MainHeader.HideNSeek");
/// <summary>
/// Base Translation: &lt;color=#4f92ff&gt;Host Only&lt;/color&gt;
/// </summary>
@@ -434,19 +434,19 @@ public static class TranslationStrings
public static readonly TranslationString BetterSetting_Setting_UseBanNameList = new("BetterSetting.Setting.UseBanNameList");
/// <summary>
/// Base Translation: Use ban word list
/// Base Translation: Use ban chat list
/// </summary>
public static readonly TranslationString BetterSetting_Setting_UseBanWordList = new("BetterSetting.Setting.UseBanWordList");
public static readonly TranslationString BetterSetting_Setting_UseBanChatList = new("BetterSetting.Setting.UseBanChatList");
/// <summary>
/// Base Translation: Only in lobby
/// </summary>
public static readonly TranslationString BetterSetting_Setting_UseBanWordListOnlyLobby = new("BetterSetting.Setting.UseBanWordListOnlyLobby");
public static readonly TranslationString BetterSetting_Setting_UseBanChatListOnlyLobby = new("BetterSetting.Setting.UseBanChatListOnlyLobby");
/// <summary>
/// Base Translation: Show role in name for clients
/// Base Translation: Ban
/// </summary>
public static readonly TranslationString BetterSetting_Setting_ShowRoleForClients = new("BetterSetting.Setting.ShowRoleForClients");
public static readonly TranslationString BetterSetting_Setting_UseBanChatListBan = new("BetterSetting.Setting.UseBanChatListBan");
/// <summary>
/// Base Translation: Censor detection reason
@@ -473,6 +473,11 @@ public static class TranslationStrings
/// </summary>
public static readonly TranslationString BetterSetting_Setting_KickLevelBelow = new("BetterSetting.Setting.KickLevelBelow");
/// <summary>
/// Base Translation: Only when player count at least
/// </summary>
public static readonly TranslationString BetterSetting_Setting_KickLevelBelowMinimumPlayers = new("BetterSetting.Setting.KickLevelBelowMinimumPlayers");
/// <summary>
/// Base Translation: Detect cheat clients
/// </summary>
@@ -493,16 +498,6 @@ public static class TranslationStrings
/// </summary>
public static readonly TranslationString BetterSetting_Setting_RateLimit = new("BetterSetting.Setting.RateLimit");
/// <summary>
/// Base Translation: Randomizer
/// </summary>
public static readonly TranslationString BetterSetting_Setting_RoleRandomizer = new("BetterSetting.Setting.RoleRandomizer");
/// <summary>
/// Base Translation: Desync roles to other clients
/// </summary>
public static readonly TranslationString BetterSetting_Setting_DesyncRoles = new("BetterSetting.Setting.DesyncRoles");
/// <summary>
/// Base Translation: Disable sabotages for dead
/// </summary>