diff --git a/BetterAmongUs.csproj b/BetterAmongUs.csproj
index 612220b..1d0bb6b 100644
--- a/BetterAmongUs.csproj
+++ b/BetterAmongUs.csproj
@@ -25,7 +25,4 @@
-
-
-
\ No newline at end of file
diff --git a/Modules/AntiCheat.cs b/Modules/AntiCheat.cs
index e98bc24..1e3a563 100644
--- a/Modules/AntiCheat.cs
+++ b/Modules/AntiCheat.cs
@@ -3,6 +3,7 @@ using HarmonyLib;
using Hazel;
using InnerNet;
using UnityEngine;
+using UnityEngine.Purchasing;
namespace BetterAmongUs;
@@ -194,7 +195,7 @@ class AntiCheat
if (player.IsAlive() && GameStates.IsInGamePlay && !GameStates.IsMeeting && !GameStates.IsExilling)
{
BetterNotificationManager.NotifyCheat(player, $"Invalid Action RPC: {Enum.GetName((RpcCalls)callId)}");
- Logger.LogCheat($"{player.Data.PlayerName} {Enum.GetName((RpcCalls)callId)}: {player.IsAlive()} - {GameStates.IsInGamePlay} - {!GameStates.IsMeeting} - {!GameStates.IsExilling}");
+ Logger.LogCheat($"{player.Data.PlayerName} {Enum.GetName((RpcCalls)callId)}: {player.IsAlive()} && {GameStates.IsInGamePlay} && {!GameStates.IsMeeting} && {!GameStates.IsExilling}");
}
return;
@@ -205,7 +206,7 @@ class AntiCheat
if ((!player.IsImpostorTeam() && Role != RoleTypes.Engineer) || player.Data.IsDead)
{
BetterNotificationManager.NotifyCheat(player, $"Invalid Action RPC: {Enum.GetName((RpcCalls)callId)}");
- Logger.LogCheat($"{player.Data.PlayerName} {Enum.GetName((RpcCalls)callId)}: {player.IsImpostorTeam()} - {Role != RoleTypes.Engineer} - {player.Data.IsDead}");
+ Logger.LogCheat($"{player.Data.PlayerName} {Enum.GetName((RpcCalls)callId)}: {player.IsImpostorTeam()} && {Role != RoleTypes.Engineer} || {player.Data.IsDead}");
}
return;
@@ -233,8 +234,8 @@ class AntiCheat
if (!player.IsImpostorTeam() || !player.IsAlive() || player.IsInVanish() || target.IsImpostorTeam())
{
BetterNotificationManager.NotifyCheat(player, $"Invalid Action RPC: {Enum.GetName((RpcCalls)callId)}");
- Logger.LogCheat($"{player.Data.PlayerName} {Enum.GetName((RpcCalls)callId)}: {!player.IsImpostorTeam()} - {player.IsInVanish()}" +
- $" - {!target.IsAlive()} - {target.IsImpostorTeam()}");
+ Logger.LogCheat($"{player.Data.PlayerName} {Enum.GetName((RpcCalls)callId)}: {!player.IsImpostorTeam()} || {player.IsInVanish()}" +
+ $" || {!target.IsAlive()} || {target.IsImpostorTeam()}");
}
}
}
@@ -266,36 +267,31 @@ class AntiCheat
BetterNotificationManager.NotifyCheat(player, $"Invalid Action RPC: {Enum.GetName((RpcCalls)callId)} - {Enum.GetName((SystemTypes)callId)}");
Logger.LogCheat($"{player.Data.PlayerName} {Enum.GetName((RpcCalls)callId)}: {!player.IsImpostorTeam()}");
- return;
- }
- if (callId is (byte)RpcCalls.UpdateSystem)
- {
- byte Type = reader.ReadByte();
- if (Type is (byte)SystemTypes.Reactor
- or (byte)SystemTypes.Laboratory
- or (byte)SystemTypes.Comms
- or (byte)SystemTypes.LifeSupp
- or (byte)SystemTypes.MushroomMixupSabotage
- or (byte)SystemTypes.HeliSabotage)
- {
- BetterNotificationManager.NotifyCheat(player, $"Invalid Action RPC: {Enum.GetName((RpcCalls)callId)} - {Enum.GetName((SystemTypes)callId)}");
- Logger.LogCheat($"{player.Data.PlayerName} {Enum.GetName((RpcCalls)callId)}: {!player.IsImpostorTeam()}");
- }
-
return;
}
}
}
- if (player.IsImpostorTeam())
+ if (callId is (byte)RpcCalls.CompleteTask)
{
- if (callId is (byte)RpcCalls.CompleteTask or (byte)RpcCalls.BootFromVent)
+ var taskId = reader.ReadPackedUInt32();
+
+ if (player.IsImpostorTeam() || !player.Data.Tasks.ToArray().Any(task => task.Id == taskId)
+ || player.BetterData().LastTaskId == taskId || player.BetterData().LastTaskId != taskId
+ && player.BetterData().TimeSinceLastTask < 1.25f)
{
BetterNotificationManager.NotifyCheat(player, $"Invalid Action RPC: {Enum.GetName((RpcCalls)callId)}");
- Logger.LogCheat($"{player.Data.PlayerName} {Enum.GetName((RpcCalls)callId)}: {player.IsImpostorTeam()}");
+ Logger.LogCheat($"{player.Data.PlayerName} {Enum.GetName((RpcCalls)callId)}: {player.IsImpostorTeam()} || {!player.Data.Tasks.ToArray().Any(task => task.Id == taskId)} ||" +
+ $" {player.BetterData().LastTaskId == taskId} || {player.BetterData().LastTaskId != taskId} && {player.BetterData().TimeSinceLastTask < 1.25f}");
+
+ player.BetterData().TimeSinceLastTask = 0f;
+ player.BetterData().LastTaskId = taskId;
return;
}
+
+ player.BetterData().TimeSinceLastTask = 0f;
+ player.BetterData().LastTaskId = taskId;
}
if (callId is (byte)RpcCalls.Pet or (byte)RpcCalls.CancelPet)
@@ -309,12 +305,37 @@ class AntiCheat
return;
}
}
- catch (Exception ex)
+ catch (Exception ex)
{
Logger.Error(ex.ToString());
}
}
+ // Check game states when sabotaging
+ public static bool RpcUpdateSystemCheck(PlayerControl player, SystemTypes systemType, byte amount)
+ {
+ if (!player.IsImpostorTeam() || !GameStates.IsSystemActive(systemType) && GameStates.IsAnySabotageActive() || Utils.SystemTypeIsSabotage(systemType)
+ || ShipStatus.Instance.AllDoors.ToArray().Any(door => !door.IsOpen))
+ {
+ BetterNotificationManager.NotifyCheat(player, $"Invalid Action RPC: {Enum.GetName(systemType)}");
+ Logger.LogCheat($"{player.Data.PlayerName} {Enum.GetName(systemType)} Start Sabotage: {!player.IsImpostorTeam()} || {!GameStates.IsSystemActive(systemType)}" +
+ $"&& {GameStates.IsAnySabotageActive()} || {Utils.SystemTypeIsSabotage(systemType)} || {ShipStatus.Instance.AllDoors.ToArray().Any(door => !door.IsOpen)}");
+ return false;
+ }
+
+ // Fix Sabo
+ if (amount == 16)
+ {
+ if (!GameStates.IsSystemActive(systemType))
+ {
+ BetterNotificationManager.NotifyCheat(player, $"Invalid Action RPC: {Enum.GetName(systemType)}");
+ Logger.LogCheat($"{player.Data.PlayerName} {Enum.GetName(systemType)} Fix Sabotage: {!GameStates.IsSystemActive(systemType)}");
+ }
+ }
+
+ return true;
+ }
+
// Check notify and cancel out request for invalid rpcs
public static bool CheckCancelRPC(PlayerControl player, byte callId, MessageReader Oldreader)
{
@@ -361,8 +382,8 @@ class AntiCheat
|| player.inMovingPlat || player.onLadder || player.MyPhysics.Animations.IsPlayingAnyLadderAnimation())
{
BetterNotificationManager.NotifyCheat(player, $"Invalid Action RPC: {Enum.GetName((RpcCalls)callId)}");
- Logger.LogCheat($"{player.Data.PlayerName} {Enum.GetName((RpcCalls)callId)}: {GameStates.IsMeeting} - {GameStates.IsHideNSeek} - {!player.IsAlive()} - {player.IsInVent()} - {player.shapeshifting}" +
- $" - {player.inMovingPlat} - {player.onLadder} - {player.MyPhysics.Animations.IsPlayingAnyLadderAnimation()}");
+ Logger.LogCheat($"{player.Data.PlayerName} {Enum.GetName((RpcCalls)callId)}: {GameStates.IsMeeting} && {GameStates.IsHideNSeek} || {!player.IsAlive()} || {player.IsInVent()} || {player.shapeshifting}" +
+ $" || {player.inMovingPlat} || {player.onLadder} || {player.MyPhysics.Animations.IsPlayingAnyLadderAnimation()}");
if (GameStates.IsHost)
{
@@ -382,7 +403,7 @@ class AntiCheat
if (!UnityEngine.Object.FindAnyObjectByType() || !deadPlayerInfo.IsDead || deadPlayerInfo == player.Data)
{
BetterNotificationManager.NotifyCheat(player, $"Invalid Action RPC: {Enum.GetName((RpcCalls)callId)}");
- Logger.LogCheat($"{player.Data.PlayerName} {Enum.GetName((RpcCalls)callId)}: {!UnityEngine.Object.FindAnyObjectByType()} - {!deadPlayerInfo.IsDead} - {deadPlayerInfo == player.Data}");
+ Logger.LogCheat($"{player.Data.PlayerName} {Enum.GetName((RpcCalls)callId)}: {!UnityEngine.Object.FindAnyObjectByType()} || {!deadPlayerInfo.IsDead} || {deadPlayerInfo == player.Data}");
if (GameStates.IsHost)
{
return false;
@@ -421,14 +442,14 @@ class AntiCheat
if (Role is not RoleTypes.Shapeshifter || !player.IsAlive())
{
BetterNotificationManager.NotifyCheat(player, $"Invalid Action RPC: {Enum.GetName((RpcCalls)callId)}");
- Logger.LogCheat($"{player.Data.PlayerName} {Enum.GetName((RpcCalls)callId)} 1: {Role is not RoleTypes.Shapeshifter} - {!player.IsAlive()}");
+ Logger.LogCheat($"{player.Data.PlayerName} {Enum.GetName((RpcCalls)callId)} 1: {Role is not RoleTypes.Shapeshifter} || {!player.IsAlive()}");
return false;
}
else if (!flag && !GameStates.IsMeeting && !GameStates.IsExilling && !player.IsInVent())
{
BetterNotificationManager.NotifyCheat(player, $"Invalid Action RPC: {Enum.GetName((RpcCalls)callId)}");
- Logger.LogCheat($"{player.Data.PlayerName} {Enum.GetName((RpcCalls)callId)} 2: {!flag} - {!GameStates.IsMeeting} - {!GameStates.IsExilling} - {!player.IsInVent()}");
+ Logger.LogCheat($"{player.Data.PlayerName} {Enum.GetName((RpcCalls)callId)} 2: {!flag} && {!GameStates.IsMeeting} && {!GameStates.IsExilling} && {!player.IsInVent()}");
return false;
}
}
@@ -438,7 +459,7 @@ class AntiCheat
if (Role is not RoleTypes.Phantom || player.Data.IsDead)
{
BetterNotificationManager.NotifyCheat(player, $"Invalid Action RPC: {Enum.GetName((RpcCalls)callId)}");
- Logger.LogCheat($"{player.Data.PlayerName} {Enum.GetName((RpcCalls)callId)} 1: {Role is not RoleTypes.Phantom} - {player.Data.IsDead}");
+ Logger.LogCheat($"{player.Data.PlayerName} {Enum.GetName((RpcCalls)callId)} 1: {Role is not RoleTypes.Phantom} || {player.Data.IsDead}");
return false;
}
@@ -466,10 +487,16 @@ class AntiCheat
if (player.DataIsCollected() == true && !GameStates.IsLocalGame && GameStates.IsVanillaServer)
{
- if (callId is (byte)RpcCalls.SetName or (byte)RpcCalls.CheckName or (byte)RpcCalls.SetLevel)
+ if (callId is (byte)RpcCalls.CheckName or (byte)RpcCalls.SetLevel)
{
BetterNotificationManager.NotifyCheat(player, $"Invalid Set RPC: {Enum.GetName((RpcCalls)callId)}");
Logger.LogCheat($"{player.Data.PlayerName} {Enum.GetName((RpcCalls)callId)}: {player.DataIsCollected() == true}");
+
+ if (callId is (byte)RpcCalls.CheckName)
+ {
+ var name = reader.ReadString();
+ Logger.LogCheat($"{player.Data.PlayerName} Has tried to change their name to '{name}' but has been undone!");
+ }
return false;
}
}
@@ -519,7 +546,7 @@ class AntiCheat
or (byte)RpcCalls.CheckVanish)
{
BetterNotificationManager.NotifyCheat(player, $"Invalid Lobby RPC: {Enum.GetName((RpcCalls)callId)}");
- Logger.LogCheat($"{player.Data.PlayerName} {Enum.GetName((RpcCalls)callId)}: {GameStates.IsInGame} - {GameStates.IsLobby}");
+ Logger.LogCheat($"{player.Data.PlayerName} {Enum.GetName((RpcCalls)callId)}: {GameStates.IsInGame} && {GameStates.IsLobby}");
return false;
}
}
diff --git a/Modules/BetterDataManager.cs b/Modules/BetterDataManager.cs
index 7d65d5f..6068fe7 100644
--- a/Modules/BetterDataManager.cs
+++ b/Modules/BetterDataManager.cs
@@ -1,10 +1,15 @@
-using System.Collections.Generic;
-using System.Text.Json;
+using System.Text.Json;
namespace BetterAmongUs;
class BetterDataManager
{
+ private static string filePath = GetFilePath("BetterData");
+ public static string filePathFolder = Path.Combine(Main.GetGamePathToAmongUs(), $"Better_Data");
+ public static string filePathFolderSaveInfo = Path.Combine(filePathFolder, $"SaveInfo");
+ public static string banPlayerListFile = Path.Combine(filePathFolderSaveInfo, "BanPlayerList.txt");
+ public static string banNameListFile = Path.Combine(filePathFolderSaveInfo, "BanNameList.txt");
+
public static string GetFilePath(string name)
{
return Path.Combine(Main.GetDataPathToAmongUs(), $"{name}.json");
@@ -12,7 +17,25 @@ class BetterDataManager
public static void SetUp()
{
- string filePath = GetFilePath("BetterData");
+ if (!Directory.Exists(filePathFolder))
+ {
+ Directory.CreateDirectory(filePathFolder);
+ }
+
+ if (!Directory.Exists(filePathFolderSaveInfo))
+ {
+ Directory.CreateDirectory(filePathFolderSaveInfo);
+ }
+
+ if (!File.Exists(banPlayerListFile))
+ {
+ File.WriteAllText(banPlayerListFile, "// Example\nFriendCode#0000\nHashPUID\n// Or\nFriendCode#0000, HashPUID");
+ }
+
+ if (!File.Exists(banNameListFile))
+ {
+ File.WriteAllText(banNameListFile, "// Example\nBanName1\nBanName2");
+ }
if (!File.Exists(filePath))
{
@@ -90,7 +113,7 @@ class BetterDataManager
return jsonData[category][name];
}
}
- catch (Exception ex)
+ catch (Exception ex)
{
Logger.Error(ex.ToString());
}
@@ -98,6 +121,37 @@ class BetterDataManager
return string.Empty;
}
+ public static void SaveBanList(string friendCode = "", string hashPUID = "")
+ {
+ if (!string.IsNullOrEmpty(friendCode) || !string.IsNullOrEmpty(hashPUID))
+ {
+ // Create the new string with the separator if both are not empty
+ string newText = string.Empty;
+
+ if (!string.IsNullOrEmpty(friendCode))
+ {
+ newText = friendCode;
+ }
+
+ if (!string.IsNullOrEmpty(hashPUID))
+ {
+ if (!string.IsNullOrEmpty(newText))
+ {
+ newText += ", ";
+ }
+ newText += Utils.GetHashPuid(hashPUID);
+ }
+
+ // Check if the file already contains the new entry
+ if (!File.Exists(banPlayerListFile) || !File.ReadLines(banPlayerListFile).Any(line => line.Equals(newText)))
+ {
+ // Append the new string to the file if it's not already present
+ File.AppendAllText(banPlayerListFile, Environment.NewLine + newText);
+ }
+ }
+ }
+
+
public static void SaveCheatData(string puid, string friendCode, string name, string category = "cheatData", string reason = "None")
{
string filePath = GetFilePath("BetterData");
@@ -251,6 +305,11 @@ class BetterDataManager
Logger.LogCheat("Cleared cheat memory and data");
}
+ public static void LoadData()
+ {
+ LoadCheatData();
+ }
+
public static void LoadCheatData()
{
try
diff --git a/Modules/BetterHostManager.cs b/Modules/BetterHostManager.cs
index 6d78d84..5e66038 100644
--- a/Modules/BetterHostManager.cs
+++ b/Modules/BetterHostManager.cs
@@ -1,8 +1,8 @@
using AmongUs.GameOptions;
using Hazel;
using InnerNet;
-using UnityEngine;
using System.Text;
+using UnityEngine;
namespace BetterAmongUs;
@@ -167,6 +167,17 @@ class BetterHostManager
}
break;
+ case (byte)RpcCalls.CompleteTask:
+ {
+ _ = new LateTask(() =>
+ {
+ RPC.SyncAllNames(force: true);
+ }, 1f, shoudLog: false);
+
+ shouldReturn = true;
+ }
+ break;
+
default:
shouldReturn = true;
break;
@@ -220,6 +231,10 @@ class BetterHostManager
else if (GameStates.IsInGamePlay)
{
string Role = $"{player.GetRoleName()}";
+ if (!player.IsImpostorTeam() && player.myTasks.Count > 0)
+ {
+ Role += $" ({player.Data.Tasks.ToArray().Where(task => task.Complete).Count()}/{player.Data.Tasks.Count})";
+ }
if (player.IsImpostorTeam() is false || target.IsImpostorTeam() is false)
{
if (target.IsAlive() && player != target)
diff --git a/Modules/DebugMenu.cs b/Modules/DebugMenu.cs
index 547edb7..6af6a2a 100644
--- a/Modules/DebugMenu.cs
+++ b/Modules/DebugMenu.cs
@@ -53,6 +53,11 @@ public class DebugMenu : MonoBehaviour
BetterDataManager.ClearCheatData();
});
+ AddButtonToTab(Main, "Send Better RPC", () =>
+ {
+ RPC.SendBetterCheck();
+ }, true);
+
AddButtonToTab(HostOnly, "Sync All Names", () =>
{
RPC.SyncAllNames();
diff --git a/Modules/Debugger.cs b/Modules/Debugger.cs
index 7cd6573..985e79f 100644
--- a/Modules/Debugger.cs
+++ b/Modules/Debugger.cs
@@ -6,7 +6,7 @@ class Logger
public static void Log(string info, string tag = "Log")
{
string mark = $"{DateTime.Now:HH:mm} [BetterLog][{tag}]";
- string logFilePath = Path.Combine(Environment.CurrentDirectory, "better-log.txt");
+ string logFilePath = Path.Combine(BetterDataManager.filePathFolder, "better-log.txt");
string newLine = $"{mark}: {info}";
File.AppendAllText(logFilePath, newLine + Environment.NewLine);
Main.Logger.LogInfo($"[{tag}] {info}");
@@ -34,7 +34,7 @@ class Logger
}
string mark = $"{DateTime.Now:HH:mm} [BetterLog][PrivateLog][{tag}]";
- string logFilePath = Path.Combine(Environment.CurrentDirectory, "better-log.txt");
+ string logFilePath = Path.Combine(BetterDataManager.filePathFolder, "better-log.txt");
string newLine = $"{mark}: " + Encryptor.Encrypt($"{info}");
File.AppendAllText(logFilePath, newLine + Environment.NewLine);
}
diff --git a/Modules/ExtendedPlayerControl.cs b/Modules/ExtendedPlayerControl.cs
index 78de8c0..168b03d 100644
--- a/Modules/ExtendedPlayerControl.cs
+++ b/Modules/ExtendedPlayerControl.cs
@@ -1,5 +1,4 @@
using AmongUs.GameOptions;
-using Hazel;
using InnerNet;
using TMPro;
using UnityEngine;
@@ -103,7 +102,7 @@ static class ExtendedPlayerControl
// Kick player
public static void Kick(this PlayerControl player, bool ban = false, string setReasonInfo = "")
{
- if (!GameStates.IsHost || !player.DataIsCollected() || player.IsHost() || player.BetterData().BannedByAntiCheat)
+ if (!GameStates.IsHost || PlayerControl.LocalPlayer == player || !player.DataIsCollected() || player.IsHost() || player.BetterData().BannedByAntiCheat || player.isDummy)
{
return;
}
@@ -164,7 +163,7 @@ static class ExtendedPlayerControl
// Check if player role name
public static string GetRoleName(this PlayerControl player)
{
- if (!player.IsAlive() && !player.IsGhostRole() && Main.GetRoleName.TryGetValue((int)player.BetterData().DeadDisplayRole, out var roleName))
+ if (!player.IsAlive() && !player.IsGhostRole() && Main.GetRoleName.TryGetValue((int)player.BetterData().RoleInfo.DeadDisplayRole, out var roleName))
{
return roleName;
}
diff --git a/Modules/ExtendedPlayerInfo.cs b/Modules/ExtendedPlayerInfo.cs
index 425bdfe..edf7f3d 100644
--- a/Modules/ExtendedPlayerInfo.cs
+++ b/Modules/ExtendedPlayerInfo.cs
@@ -10,35 +10,54 @@ public class ExtendedPlayerInfo
public bool IsBetterHost { get; set; } = false;
public bool IsTOHEHost { get; set; } = false;
public bool BannedByAntiCheat { get; set; } = false;
- public bool HasNoisemakerNotify { get; set; } = false;
+
+ // Track Game Info
+ public float TimeSinceLastTask { get; set; } = 5f;
+ public uint LastTaskId { get; set; } = 0;
public int TimesCalledMeeting { get; set; } = 0;
+ public DisconnectReasons DisconnectReason { get; set; } = DisconnectReasons.Unknown;
+ public ExtendedRoleInfo? RoleInfo { get; set; }
+}
+
+public class ExtendedRoleInfo
+{
+ public int Kills { get; set; } = 0;
+ public bool HasNoisemakerNotify { get; set; } = false;
public RoleTypes DeadDisplayRole { get; set; }
}
public static class PlayerControlExtensions
{
- private static readonly Dictionary playerInfo = [];
+ public static readonly Dictionary playerInfo = [];
// Get BetterData from PlayerControl
- public static ExtendedPlayerInfo BetterData(this PlayerControl player)
+ public static ExtendedPlayerInfo? BetterData(this PlayerControl player)
{
- if (!playerInfo.ContainsKey(player.Data))
+ if (player == null) return null;
+
+ if (!playerInfo.ContainsKey(player.Data.Puid))
{
- playerInfo[player.Data] = new ExtendedPlayerInfo();
+ playerInfo[player.Data.Puid] = new ExtendedPlayerInfo
+ {
+ RoleInfo = new ExtendedRoleInfo()
+ };
}
- return playerInfo[player.Data];
+ return playerInfo[player.Data.Puid];
}
// Get BetterData from NetworkedPlayerInfo
- public static ExtendedPlayerInfo BetterData(this NetworkedPlayerInfo info)
+ public static ExtendedPlayerInfo? BetterData(this NetworkedPlayerInfo info)
{
- if (!playerInfo.ContainsKey(info))
+ if (!playerInfo.ContainsKey(info.Puid))
{
- playerInfo[info] = new ExtendedPlayerInfo();
+ playerInfo[info.Puid] = new ExtendedPlayerInfo
+ {
+ RoleInfo = new ExtendedRoleInfo()
+ };
}
- return playerInfo[info];
+ return playerInfo[info.Puid];
}
// Get BetterData from ClientData
@@ -48,12 +67,15 @@ public static class PlayerControlExtensions
if (player != null)
{
- if (!playerInfo.ContainsKey(player.Data))
+ if (!playerInfo.ContainsKey(player.Data.Puid))
{
- playerInfo[player.Data] = new ExtendedPlayerInfo();
+ playerInfo[player.Data.Puid] = new ExtendedPlayerInfo
+ {
+ RoleInfo = new ExtendedRoleInfo()
+ };
}
- return playerInfo[player.Data];
+ return playerInfo[player.Data.Puid];
}
return null;
diff --git a/Modules/FileChecker.cs b/Modules/FileChecker.cs
index 147197a..550263a 100644
--- a/Modules/FileChecker.cs
+++ b/Modules/FileChecker.cs
@@ -7,7 +7,7 @@ class FileChecker
{
private static bool Enabled = true;
private static readonly ReadOnlyCollection UnsupportedBepInExMods = new(new List { "TOHE", "YuAntiCheat" }); // Put BepInEx BepInPlugin name, not dll name here lol.
- private static readonly ReadOnlyCollection BannedBepInExMods = new(new List { "MalumMenu", "MalumMenu-Yu", "MalumMenuYu", "AUnlocker" }); // Put BepInEx BepInPlugin name, not dll name here lol.
+ private static readonly ReadOnlyCollection BannedBepInExMods = new(new List { "MalumMenu", "MalumMenu-Yu", "MalumMenuYu"/*, "AUnlocker" */}); // Put BepInEx BepInPlugin name, not dll name here lol.
private static readonly ReadOnlyCollection KeyWordsInVersionInfo = new(new List { "Malum", "Sicko", "AUM" }); // Banned words for version text
public static string UnauthorizedReason = string.Empty;
public static List CheatTags = []; // For API report
@@ -232,7 +232,7 @@ class FileChecker
{
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
- if (assembly.FullName.Contains(modName))
+ if (assembly.FullName.Contains(modName, StringComparison.OrdinalIgnoreCase))
{
return true;
}
diff --git a/Modules/GameState.cs b/Modules/GameState.cs
index 6baa41c..068c4f8 100644
--- a/Modules/GameState.cs
+++ b/Modules/GameState.cs
@@ -1,5 +1,4 @@
using AmongUs.GameOptions;
-using BetterAmongUs.Patches;
namespace BetterAmongUs;
@@ -18,6 +17,67 @@ public static class GameStates
public static bool DleksIsActive => (MapNames)GameOptionsManager.Instance.CurrentGameOptions.MapId == MapNames.Dleks;
public static bool AirshipIsActive => (MapNames)GameOptionsManager.Instance.CurrentGameOptions.MapId == MapNames.Airship;
public static bool FungleIsActive => (MapNames)GameOptionsManager.Instance.CurrentGameOptions.MapId == MapNames.Fungle;
+ public static byte GetActiveMapId => GameOptionsManager.Instance.CurrentGameOptions.MapId;
+ public static bool IsSystemActive(SystemTypes type)
+ {
+ if (IsHideNSeek || !ShipStatus.Instance.Systems.TryGetValue(type, out var system))
+ {
+ return false;
+ }
+
+ int mapId = GetActiveMapId;
+
+ return type switch
+ {
+ SystemTypes.Electrical when mapId != 5 => system.Cast()?.IsActive ?? false,
+ SystemTypes.Reactor when mapId != 2 => system.Cast()?.IsActive ?? false,
+ SystemTypes.Laboratory when mapId == 2 => system.Cast()?.IsActive ?? false,
+ SystemTypes.LifeSupp when mapId is 0 or 3 => system.Cast()?.IsActive ?? false,
+ SystemTypes.HeliSabotage when mapId == 4 => system.Cast()?.IsActive ?? false,
+ SystemTypes.Comms when mapId is 1 or 5 => system.Cast()?.IsActive ?? false,
+ SystemTypes.Comms => system.Cast()?.IsActive ?? false,
+ SystemTypes.MushroomMixupSabotage when mapId == 5 => system.Cast()?.IsActive ?? false,
+ _ => false
+ };
+ }
+ public static bool IsCriticalSabotageActive()
+ {
+ var deathSabotages = new[]
+ {
+ SystemTypes.Reactor,
+ SystemTypes.Laboratory,
+ SystemTypes.LifeSupp,
+ SystemTypes.HeliSabotage,
+ };
+
+ return deathSabotages.Any(IsSystemActive);
+ }
+ public static bool IsNoneCriticalSabotageActive()
+ {
+ var noneDeathSabotages = new[]
+ {
+ SystemTypes.Electrical,
+ SystemTypes.Comms,
+ SystemTypes.MushroomMixupSabotage
+ };
+
+ return noneDeathSabotages.Any(IsSystemActive);
+ }
+ public static bool IsAnySabotageActive()
+ {
+ var allSabotages = new[]
+ {
+ SystemTypes.Electrical,
+ SystemTypes.Reactor,
+ SystemTypes.Laboratory,
+ SystemTypes.LifeSupp,
+ SystemTypes.HeliSabotage,
+ SystemTypes.Comms,
+ SystemTypes.MushroomMixupSabotage
+ };
+
+ return allSabotages.Any(IsSystemActive);
+ }
public static bool IsInGame => InGame;
public static bool IsLobby => AmongUsClient.Instance?.GameState == InnerNet.InnerNetClient.GameStates.Joined;
public static bool IsInIntro => IntroCutscene.Instance != null;
diff --git a/Modules/RPC.cs b/Modules/RPC.cs
index 6f07b33..10c8fea 100644
--- a/Modules/RPC.cs
+++ b/Modules/RPC.cs
@@ -1,6 +1,7 @@
using BetterAmongUs.Patches;
using HarmonyLib;
using Hazel;
+using InnerNet;
namespace BetterAmongUs;
@@ -55,14 +56,42 @@ internal class RPCHandlerPatch
{
RPC.HandleCustomRPC(__instance, callId, reader);
}
+}
+
+[HarmonyPatch(typeof(ShipStatus), nameof(ShipStatus.UpdateSystem), typeof(SystemTypes), typeof(PlayerControl), typeof(MessageReader))]
+public static class MessageReaderUpdateSystemPatch
+{
+ public static bool Prefix(/*ShipStatus __instance,*/ [HarmonyArgument(0)] SystemTypes systemType, [HarmonyArgument(1)] PlayerControl player, [HarmonyArgument(2)] MessageReader reader)
+ {
+ if (GameStates.IsHideNSeek) return false;
+
+ var amount = MessageReader.Get(reader).ReadByte();
+ if (AntiCheat.RpcUpdateSystemCheck(player, systemType, amount) != true)
+ {
+ Logger.LogCheat($"RPC canceled by Anti-Cheat: {Enum.GetName(typeof(SystemTypes), (int)systemType)} - {amount}");
+ return false;
+ }
+
+ return true;
+ }
}
internal static class RPC
{
+ public static void SendBetterCheck()
+ {
+ var flag = GameStates.IsHost && Main.BetterHost.Value;
+ MessageWriter messageWriter = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.BetterCheck, SendOption.None, -1);
+ messageWriter.Write(true);
+ messageWriter.Write(flag);
+ messageWriter.Write(Main.GetVersionText().Replace(" ", ""));
+ AmongUsClient.Instance.FinishRpcImmediately(messageWriter);
+ }
+
public static void SetNamePrivate(PlayerControl player, string name, PlayerControl target)
{
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(player.NetId, (byte)RpcCalls.SetName, SendOption.Reliable, target.GetClientId());
- writer.Write(player.NetId);
+ writer.Write(player.Data.NetId);
writer.Write(name);
AmongUsClient.Instance.FinishRpcImmediately(writer);
}
@@ -138,17 +167,17 @@ internal static class RPC
switch (callId)
{
case (byte)CustomRPC.BetterCheck:
- if (reader.ReadByte() == player.Data.NetId)
{
+ var SetBetterUser = reader.ReadBoolean();
var IsBetterHost = reader.ReadBoolean();
if (!player.IsHost() && IsBetterHost)
{
- BetterNotificationManager.NotifyCheat(player, $"Invalid Action RPC: {Enum.GetName((RpcCalls)callId)} called as BetterHost");
+ BetterNotificationManager.NotifyCheat(player, $"Invalid Action RPC: {Enum.GetName((CustomRPC)callId)} called as BetterHost");
break;
}
- player.BetterData().IsBetterUser = true;
+ player.BetterData().IsBetterUser = SetBetterUser;
player.BetterData().IsBetterHost = IsBetterHost;
}
break;
diff --git a/Modules/Utils.cs b/Modules/Utils.cs
index 0fc6afd..ea46f90 100644
--- a/Modules/Utils.cs
+++ b/Modules/Utils.cs
@@ -63,6 +63,18 @@ public static class Utils
throw;
}
}
+ public static bool SystemTypeIsSabotage(SystemTypes type) => type is SystemTypes.Reactor
+ or SystemTypes.Laboratory
+ or SystemTypes.Comms
+ or SystemTypes.LifeSupp
+ or SystemTypes.MushroomMixupSabotage
+ or SystemTypes.HeliSabotage;
+ public static bool SystemTypeIsSabotage(int typeNum) => (SystemTypes)typeNum is SystemTypes.Reactor
+ or SystemTypes.Laboratory
+ or SystemTypes.Comms
+ or SystemTypes.LifeSupp
+ or SystemTypes.MushroomMixupSabotage
+ or SystemTypes.HeliSabotage;
// Get players HashPuid
public static string GetHashPuid(PlayerControl player)
{
@@ -94,6 +106,18 @@ public static class Utils
}
// Get name for role
public static string GetRoleName(RoleTypes role) => Main.GetRoleName[(int)role];
+ // Get hex color for team
+ public static string GetTeamHexColor(RoleTeamTypes team)
+ {
+ if (team == RoleTeamTypes.Impostor)
+ {
+ return "#f00202";
+ }
+ else
+ {
+ return "#8cffff";
+ }
+ }
public static string Color32ToHex(Color32 color) => $"#{color.r:X2}{color.g:X2}{color.b:X2}{255:X2}";
// Disconnect client
public static void DisconnectSelf(string reason, bool showReason = true)
diff --git a/Patches/ClientOptionsPatch.cs b/Patches/ClientOptionsPatch.cs
index 4ee396e..5702c0f 100644
--- a/Patches/ClientOptionsPatch.cs
+++ b/Patches/ClientOptionsPatch.cs
@@ -1,5 +1,4 @@
using HarmonyLib;
-using Hazel;
using UnityEngine;
namespace BetterAmongUs.Patches;
@@ -52,12 +51,7 @@ public static class OptionsMenuBehaviourPatch
BetterHost = ClientOptionItem.Create("Better Host", Main.BetterHost, __instance, BetterHostButtonToggle, () => !toggleCheckInGamePlay("Better Host"));
static void BetterHostButtonToggle()
{
- var flag = GameStates.IsHost && Main.BetterHost.Value;
- MessageWriter messageWriter = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, unchecked((byte)CustomRPC.BetterCheck), SendOption.None, -1);
- messageWriter.Write((byte)PlayerControl.LocalPlayer.NetId);
- messageWriter.Write(flag);
- messageWriter.Write(Main.GetVersionText().Replace(" ", ""));
- AmongUsClient.Instance.FinishRpcImmediately(messageWriter);
+ RPC.SendBetterCheck();
RPC.SyncAllNames(force: true);
}
@@ -66,8 +60,8 @@ public static class OptionsMenuBehaviourPatch
if (BetterRoleAlgorithma == null || BetterRoleAlgorithma.ToggleButton == null)
{
BetterRoleAlgorithma = ClientOptionItem.Create("Better Role Algorithma", Main.BetterRoleAlgorithma, __instance, toggleCheck: () => !toggleCheckInGamePlay("Better Role Algorithma"));
- }
-
+ }
+
if (BetterNotifications == null || BetterNotifications.ToggleButton == null)
{
BetterNotifications = ClientOptionItem.Create("Better Notifications", Main.BetterNotifications, __instance, BetterNotificationsToggle);
diff --git a/Patches/ClientPatch.cs b/Patches/ClientPatch.cs
index 9cb7890..11d194b 100644
--- a/Patches/ClientPatch.cs
+++ b/Patches/ClientPatch.cs
@@ -73,6 +73,16 @@ public class ClientPatch
__result = GameStates.IsHost || (GameStates.IsInGamePlay && (GameStates.IsMeeting || GameStates.IsExilling));
return false;
}
+ [HarmonyPatch(nameof(InnerNetClient.KickPlayer))]
+ [HarmonyPrefix]
+ public static void KickPlayer_Prefix(ref int clientId, ref bool ban)
+ {
+ if (ban)
+ {
+ NetworkedPlayerInfo info = Utils.PlayerFromClientId(clientId).Data;
+ BetterDataManager.SaveBanList(info.FriendCode, info.Puid);
+ }
+ }
}
// Set text color
[HarmonyPatch(typeof(CosmeticsLayer))]
@@ -146,7 +156,6 @@ public class ClientPatch
string extractedText = playerName.Split(new[] { "", "" }, StringSplitOptions.None)[1];
playerName = extractedText;
}
-
}
}
}
diff --git a/Patches/CommandsPatch.cs b/Patches/CommandsPatch.cs
index 286a410..5712f37 100644
--- a/Patches/CommandsPatch.cs
+++ b/Patches/CommandsPatch.cs
@@ -107,7 +107,7 @@ class CommandsPatch
case "dump":
if (GameStates.IsInGamePlay && !GameStates.IsDev) return;
- string logFilePath = Path.Combine(Environment.CurrentDirectory, "better-log.txt");
+ string logFilePath = Path.Combine(BetterDataManager.filePathFolder, "better-log.txt");
string log = File.ReadAllText(logFilePath);
string newLog = string.Empty;
string[] logArray = log.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
diff --git a/Patches/GamePlayManagerPatch.cs b/Patches/GamePlayManagerPatch.cs
index d366ea0..e8a3ae6 100644
--- a/Patches/GamePlayManagerPatch.cs
+++ b/Patches/GamePlayManagerPatch.cs
@@ -1,5 +1,8 @@
using HarmonyLib;
-using Hazel;
+using Newtonsoft.Json.Utilities;
+using System.Text;
+using TMPro;
+using UnityEngine;
namespace BetterAmongUs.Patches;
@@ -16,6 +19,23 @@ class GamePlayManager
{
AntiCheat.PauseAntiCheat();
}
+
+ // Clear unused Better Data
+ var keysToRemove = new List();
+
+ foreach (var betterInfo in PlayerControlExtensions.playerInfo)
+ {
+ if (!Main.AllPlayerControls.Any(pc => pc.Data.Puid == betterInfo.Key))
+ {
+ keysToRemove.Add(betterInfo.Key);
+ }
+ }
+
+ foreach (var key in keysToRemove)
+ {
+ PlayerControlExtensions.playerInfo.Remove(key);
+ }
+
}
[HarmonyPatch(nameof(LobbyBehaviour.Start))]
[HarmonyPostfix]
@@ -26,6 +46,7 @@ class GamePlayManager
if (GameStates.IsInGame)
{
RPC.SyncAllNames(force: true);
+ RPC.SendBetterCheck();
}
}, 1.5f, "LobbyBehaviourPatch SyncAllNames");
}
@@ -62,12 +83,206 @@ class GamePlayManager
{
[HarmonyPatch(nameof(GameStartManager.Update))]
[HarmonyPrefix]
- private static void Prefix(GameStartManager __instance)
+ private static void Update_Prefix(GameStartManager __instance)
{
- if (GameStates.IsDev)
+ __instance.MinPlayers = 1;
+ }
+ [HarmonyPatch(nameof(GameStartManager.Update))]
+ [HarmonyPostfix]
+ private static void Update_Postfix(GameStartManager __instance)
+ {
+ if (!GameStates.IsHost)
{
- __instance.MinPlayers = 1;
+ __instance.StartButton.gameObject.SetActive(false);
+ return;
+
}
+ __instance.GameStartTextParent.SetActive(false);
+ __instance.StartButton.gameObject.SetActive(true);
+ if (__instance.startState == GameStartManager.StartingStates.Countdown)
+ {
+ __instance.StartButton.buttonText.text = string.Format("Cancel: {0}", (int)__instance.countDownTimer + 1);
+ }
+ else
+ {
+ __instance.StartButton.buttonText.text = "Start Game";
+ }
+ }
+ [HarmonyPatch(nameof(GameStartManager.BeginGame))]
+ [HarmonyPrefix]
+ private static bool BeginGame_Prefix(GameStartManager __instance)
+ {
+ if (__instance.startState == GameStartManager.StartingStates.Countdown)
+ {
+ SoundManager.instance.StopSound(__instance.gameStartSound);
+ __instance.ResetStartState();
+ return false;
+ }
+
+ if (Input.GetKey(KeyCode.LeftShift))
+ {
+ __instance.startState = GameStartManager.StartingStates.Countdown;
+ __instance.FinallyBegin();
+ return false;
+ }
+
+ return true;
+ }
+ }
+ [HarmonyPatch(typeof(EndGameManager))]
+ public class EndGameManagerPatch
+ {
+ [HarmonyPatch(nameof(EndGameManager.SetEverythingUp))]
+ [HarmonyPostfix]
+ private static void SetEverythingUp_Postfix(EndGameManager __instance)
+ {
+ GameObject SummaryObj = UnityEngine.Object.Instantiate(__instance.WinText.gameObject, __instance.WinText.transform.parent.transform);
+ SummaryObj.name = "SummaryObj (TMP)";
+ SummaryObj.transform.SetSiblingIndex(0);
+ Camera localCamera;
+ if (DestroyableSingleton.InstanceExists)
+ {
+ localCamera = DestroyableSingleton.Instance.GetComponentInChildren();
+ }
+ else
+ {
+ localCamera = Camera.main;
+ }
+
+ SummaryObj.transform.position = AspectPosition.ComputeWorldPosition(localCamera, AspectPosition.EdgeAlignments.LeftTop, new Vector3(1f, 0.2f, -5f));
+ SummaryObj.transform.localScale = new Vector3(0.22f, 0.22f, 0.22f);
+ TextMeshPro SummaryText = SummaryObj.GetComponent();
+ if (SummaryText != null)
+ {
+ SummaryText.autoSizeTextContainer = false;
+ SummaryText.enableAutoSizing = false;
+ SummaryText.lineSpacing = -25f;
+ SummaryText.alignment = TextAlignmentOptions.TopLeft;
+ SummaryText.color = Color.white;
+
+ NetworkedPlayerInfo[] playersData = GameData.Instance.AllPlayers
+ .ToArray()
+ .OrderBy(pd => pd.Disconnected) // Disconnected players last
+ .ThenBy(pd => pd.IsDead)
+ .ThenBy(pd => !pd.Role.IsImpostor)
+ .ToArray(); // Dead players after live players
+
+ string winTeam;
+ string winTag;
+ string winColor;
+
+ switch (EndGameResult.CachedGameOverReason)
+ {
+ case GameOverReason.HumansByTask:
+ winTeam = "Cremates";
+ winTag = "Tasks Completion";
+ winColor = "#8cffff";
+ break;
+ case GameOverReason.HumansByVote:
+ winTeam = "Cremates";
+ winTag = "Imposters Voted Out";
+ winColor = "#8cffff";
+ break;
+ case GameOverReason.ImpostorDisconnect:
+ winTeam = "Cremates";
+ winTag = "Impostors Disconnected";
+ winColor = "#8cffff";
+ break;
+ case GameOverReason.ImpostorByKill:
+ winTeam = "Imposters";
+ winTag = "Crew Outnumbered";
+ winColor = "#f00202";
+ break;
+ case GameOverReason.ImpostorBySabotage:
+ winTeam = "Imposters";
+ winTag = "Sabotage";
+ winColor = "#f00202";
+ break;
+ case GameOverReason.ImpostorByVote:
+ winTeam = "Imposters";
+ winTag = "Crew Outnumbered";
+ winColor = "#f00202";
+ break;
+ case GameOverReason.HumansDisconnect:
+ winTeam = "Imposters";
+ winTag = "Cremates Disconnected";
+ winColor = "#f00202";
+ break;
+
+ // H&S
+
+ case GameOverReason.HideAndSeek_ByTimer:
+ winTeam = "Hiders";
+ winTag = "Time Out";
+ winColor = "#8cffff";
+ break;
+ case GameOverReason.HideAndSeek_ByKills:
+ winTeam = "Seekers";
+ winTag = "No Survivors";
+ winColor = "#f00202";
+ break;
+
+
+ default:
+ winTeam = "Unknown";
+ winTag = "Unknown";
+ winColor = "#ffffff";
+ break;
+ }
+
+ string SummaryHeader = " Game Summary";
+ SummaryHeader += $"\n\n{winTeam} Won" +
+ $"\n\nBy {winTag}";
+
+ StringBuilder sb = new StringBuilder();
+
+ foreach (var data in playersData)
+ {
+ var name = $"{data.PlayerName}";
+ string playerTheme(string text) => $"{text}";
+
+ string roleInfo;
+ if (data.Role.IsImpostor)
+ {
+ roleInfo = $"({playerTheme(Utils.GetRoleName(data.RoleType))}) → {playerTheme($"Kills: {data.BetterData().RoleInfo.Kills}")}";
+ }
+ else
+ {
+ roleInfo = $"({playerTheme(Utils.GetRoleName(data.RoleType))}) → {playerTheme($"Tasks: {data.Tasks.ToArray().Where(task => task.Complete).Count()}/{data.Tasks.Count}")}";
+ }
+
+ string deathReason;
+ if (data.Disconnected)
+ {
+ deathReason = "『D/C』";
+ }
+ else if (!data.IsDead)
+ {
+ deathReason = "『Alive』";
+ ;
+ }
+ else if (data.IsDead)
+ {
+ deathReason = "『Dead』";
+ }
+ else
+ {
+ deathReason = "『Unknown』";
+ }
+
+ sb.AppendLine($"- {name} {roleInfo} {deathReason}\n");
+ }
+
+ SummaryText.text = $"{SummaryHeader}\n\n{sb}";
+ }
+ }
+ [HarmonyPatch(nameof(EndGameManager.ShowButtons))]
+ [HarmonyPrefix]
+ private static bool ShowButtons_Prefix(EndGameManager __instance)
+ {
+ __instance.FrontMost.gameObject.SetActive(false);
+ __instance.Navigation.ShowDefaultNavigation();
+ return false;
}
}
}
diff --git a/Patches/MeetingHudPatch.cs b/Patches/MeetingHudPatch.cs
index 1822659..b9a80f2 100644
--- a/Patches/MeetingHudPatch.cs
+++ b/Patches/MeetingHudPatch.cs
@@ -1,5 +1,4 @@
using HarmonyLib;
-using Hazel;
using System.Text;
using TMPro;
using UnityEngine;
@@ -77,7 +76,35 @@ class MeetingHudUpdatePatch
if (!flag)
{
- SetPlayerTextInfoMeeting(pva, "Disconnected", isInfo: true);
+ string DisconnectText;
+ var playerData = GameData.Instance.GetPlayerById(pva.TargetPlayerId);
+ switch (playerData.BetterData().DisconnectReason)
+ {
+ case DisconnectReasons.ExitGame:
+ DisconnectText = "Left The Game";
+ break;
+ case DisconnectReasons.Banned:
+ if (playerData.BetterData().BannedByAntiCheat)
+ {
+ DisconnectText = "Banned By Anti-Cheat>";
+ }
+ else
+ {
+ DisconnectText = "Banned By Host";
+ }
+ break;
+ case DisconnectReasons.Kicked:
+ DisconnectText = "Kicked By Host";
+ break;
+ case DisconnectReasons.Hacking:
+ DisconnectText = "Banned By Server";
+ break;
+ default:
+ DisconnectText = "Disconnected";
+ break;
+ }
+
+ SetPlayerTextInfoMeeting(pva, $"{DisconnectText}", isInfo: true);
SetPlayerTextInfoMeeting(pva, "");
pva.transform.Find("votePlayerBase").gameObject.SetActive(false);
pva.transform.Find("deadX_border").gameObject.SetActive(false);
@@ -120,6 +147,10 @@ class MeetingHudUpdatePatch
string RoleHexColor = target.IsImpostorTeam() ? "#ff1919" : "#8cffff";
string Role = $"{target.GetRoleName()}";
+ if (!target.IsImpostorTeam() && target.myTasks.Count > 0)
+ {
+ Role += $" ({target.myTasks.ToArray().Where(task => task.IsComplete).Count()}/{target.myTasks.Count})";
+ }
if (!target.IsImpostorTeammate())
{
if (PlayerControl.LocalPlayer.IsAlive() && target != PlayerControl.LocalPlayer)
diff --git a/Patches/PlayerControlPatch.cs b/Patches/PlayerControlPatch.cs
index 26f39f5..8085820 100644
--- a/Patches/PlayerControlPatch.cs
+++ b/Patches/PlayerControlPatch.cs
@@ -71,7 +71,14 @@ class PlayerControlPatch
if (!GameStates.IsInGamePlay)
{
__instance.BetterData().TimesCalledMeeting = 0;
- __instance.BetterData().HasNoisemakerNotify = false;
+ __instance.BetterData().RoleInfo.HasNoisemakerNotify = false;
+ __instance.BetterData().TimeSinceLastTask = 5f;
+ __instance.BetterData().LastTaskId = 999;
+ __instance.BetterData().RoleInfo.Kills = 0;
+ }
+ else
+ {
+ __instance.BetterData().TimeSinceLastTask += Time.deltaTime;
}
}
@@ -103,6 +110,8 @@ class PlayerControlPatch
string NewName = player.Data.PlayerName;
string hashPuid = Utils.GetHashPuid(player);
+ string platform = Utils.GetPlatformName(player, useTag: true);
+
string pattern = @"^[a-zA-Z0-9#]+$";
string hashtagPattern = @"^#[0-9]{4}$";
string friendCode = player.Data.FriendCode;
@@ -112,11 +121,14 @@ class PlayerControlPatch
|| friendCode.Contains(' ')) ? "#00f7ff" : "#ff0000";
if (string.IsNullOrEmpty(friendCode) || friendCode == "")
{
- friendCode = "???";
+ friendCode = "No Friend Code";
friendCodeColor = "#ff0000";
}
if (DataManager.Settings.Gameplay.StreamerMode == true)
+ {
friendCode = string.Concat('*').Repeat(friendCode.Length);
+ platform = "Platform Hidden";
+ }
var sbTag = new StringBuilder();
var sbInfo = new StringBuilder();
@@ -126,12 +138,15 @@ class PlayerControlPatch
var sbInfoBottom = new StringBuilder();
// Put +++ at the end of each tag
- if (!string.IsNullOrEmpty(hashPuid) && AntiCheat.SickoData.ContainsKey(hashPuid) || !string.IsNullOrEmpty(friendCode) && AntiCheat.SickoData.ContainsValue(friendCode))
- sbTag.Append("Sicko User+++");
- else if (!string.IsNullOrEmpty(hashPuid) && AntiCheat.AUMData.ContainsKey(hashPuid) || !string.IsNullOrEmpty(friendCode) && AntiCheat.AUMData.ContainsValue(friendCode))
- sbTag.Append("AUM User+++");
- else if (!string.IsNullOrEmpty(hashPuid) && AntiCheat.PlayerData.ContainsKey(hashPuid) || !string.IsNullOrEmpty(friendCode) && AntiCheat.PlayerData.ContainsValue(friendCode))
- sbTag.Append("Known Cheater+++");
+ if (!player.isDummy)
+ {
+ if (!string.IsNullOrEmpty(hashPuid) && AntiCheat.SickoData.ContainsKey(hashPuid) || !string.IsNullOrEmpty(friendCode) && AntiCheat.SickoData.ContainsValue(friendCode))
+ sbTag.Append("Sicko User+++");
+ else if (!string.IsNullOrEmpty(hashPuid) && AntiCheat.AUMData.ContainsKey(hashPuid) || !string.IsNullOrEmpty(friendCode) && AntiCheat.AUMData.ContainsValue(friendCode))
+ sbTag.Append("AUM User+++");
+ else if (!string.IsNullOrEmpty(hashPuid) && AntiCheat.PlayerData.ContainsKey(hashPuid) || !string.IsNullOrEmpty(friendCode) && AntiCheat.PlayerData.ContainsValue(friendCode))
+ sbTag.Append("Known Cheater+++");
+ }
if (GameStates.IsInGame && GameStates.IsLobby && !GameStates.IsFreePlay)
{
@@ -145,13 +160,14 @@ class PlayerControlPatch
if (player.IsDev() && !GameStates.IsInGamePlay)
sbTag.Append("(Dev)+++");
- if (((player == PlayerControl.LocalPlayer && GameStates.IsHost && Main.BetterHost.Value) || player.BetterData().IsBetterHost) && !GameStates.IsInGamePlay)
+ if (((player == PlayerControl.LocalPlayer && GameStates.IsHost && Main.BetterHost.Value)
+ || (player != PlayerControl.LocalPlayer && player.BetterData().IsBetterHost && player.IsHost())) && !GameStates.IsInGamePlay)
sbTag.Append("Better Host+++");
else if ((player == PlayerControl.LocalPlayer || player.BetterData().IsBetterUser) && !GameStates.IsInGamePlay)
sbTag.Append("Better User+++");
sbTag.Append($"ID: {player.PlayerId}+++");
- sbTagTop.Append($"{Utils.GetPlatformName(player, useTag: true)}+++");
+ sbTagTop.Append($"{platform}+++");
sbTagTop.Append($"Lv: {player.Data.PlayerLevel.ToString()}+++");
@@ -162,6 +178,10 @@ class PlayerControlPatch
if (player.IsImpostorTeammate() || player == PlayerControl.LocalPlayer || !PlayerControl.LocalPlayer.IsAlive() || DebugMenu.RevealRoles)
{
string Role = $"{player.GetRoleName()}";
+ if (!player.IsImpostorTeam() && player.myTasks.Count > 0)
+ {
+ Role += $" ({player.Data.Tasks.ToArray().Where(task => task.Complete).Count()}/{player.Data.Tasks.Count})";
+ }
sbTagTop.Append($"{Role}+++");
}
}
@@ -204,7 +224,8 @@ class PlayerControlPatch
player.SetPlayerTextInfo(sbInfoTop.ToString());
player.SetPlayerTextInfo(sbInfoBottom.ToString(), isBottom: true);
player.SetPlayerTextInfo(sbInfo.ToString(), isInfo: true);
- } catch (Exception ex)
+ }
+ catch (Exception ex)
{
Logger.Error(ex.ToString());
}
@@ -234,6 +255,8 @@ class PlayerControlPatch
HudManager.Instance?.NotifyOfDeath();
Logger.LogPrivate($"{__instance.Data.PlayerName} Has killed {target.Data.PlayerName} as {Utils.GetRoleName(__instance.Data.RoleType)}", "EventLog");
+
+ __instance.BetterData().RoleInfo.Kills += 1;
}
[HarmonyPatch(nameof(PlayerControl.Shapeshift))]
[HarmonyPostfix]
diff --git a/Patches/PlayerJoinAndLeftPatch.cs b/Patches/PlayerJoinAndLeftPatch.cs
index 4b55074..6fa34fa 100644
--- a/Patches/PlayerJoinAndLeftPatch.cs
+++ b/Patches/PlayerJoinAndLeftPatch.cs
@@ -1,6 +1,5 @@
using BetterAmongUs.Patches;
using HarmonyLib;
-using Hazel;
using InnerNet;
namespace BetterAmongUs;
@@ -12,6 +11,8 @@ class OnGameJoinedPatch
{
try
{
+ PlayerControlExtensions.playerInfo.Clear();
+
PlayerControlPatch.infotime = 0f;
AntiCheat.PauseAntiCheat();
@@ -24,20 +25,6 @@ class OnGameJoinedPatch
host.SetColor(host.CurrentOutfit.ColorId);
}
- _ = new LateTask(() =>
- {
- // Send Better Among Us Check RPC
- if (GameStates.IsInGame)
- {
- var flag = GameStates.IsHost && Main.BetterHost.Value;
- MessageWriter messageWriter = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.BetterCheck, SendOption.None, -1);
- messageWriter.Write((byte)PlayerControl.LocalPlayer.NetId);
- messageWriter.Write(flag);
- messageWriter.Write(Main.GetVersionText().Replace(" ", ""));
- AmongUsClient.Instance.FinishRpcImmediately(messageWriter);
- }
- }, 1.5f, "OnGameJoinedPatch");
-
Logger.Log($"Successfully joined {GameCode.IntToGameName(AmongUsClient.Instance.GameId)}", "OnGameJoinedPatch");
}
catch { };
@@ -55,17 +42,54 @@ public static class OnPlayerJoinedPatch
if (GameStates.IsInGame)
{
// Send Better Among Us Check RPC
- var flag = GameStates.IsHost && Main.BetterHost.Value;
- MessageWriter messageWriter = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, unchecked((byte)CustomRPC.BetterCheck), SendOption.None, -1);
- messageWriter.Write((byte)PlayerControl.LocalPlayer.NetId);
- messageWriter.Write(flag);
- messageWriter.Write(Main.GetVersionText().Replace(" ", ""));
- AmongUsClient.Instance.FinishRpcImmediately(messageWriter);
+ RPC.SendBetterCheck();
RPC.SyncAllNames(force: true);
if (Main.BetterHost.Value)
client.Character.RpcSendHostChat(HudManagerPatch.WelcomeMessage, sendToBetterUser: false);
+
+ // Auto ban player on ban list
+ var player = Utils.PlayerFromClientId(client.Id);
+ if (player != null)
+ {
+ string banPlayerListContent = File.ReadAllText(BetterDataManager.banPlayerListFile);
+
+ string[] listPlayerArray = banPlayerListContent.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
+
+ foreach (string text in listPlayerArray)
+ {
+ if (!string.IsNullOrEmpty(player.Data.FriendCode) && text.Contains(player.Data.FriendCode)
+ || !string.IsNullOrEmpty(Utils.GetHashPuid(player)) && text.Contains(Utils.GetHashPuid(player)))
+ {
+ player.Kick(true, $"{player.Data.PlayerName} has been banned due to being on the ban player list!");
+ break;
+ }
+ }
+
+ // Normalize and remove spaces and special characters from names
+ Func normalizeName = name => new string(name.Where(c => char.IsLetterOrDigit(c)).ToArray()).ToLower();
+
+ // Read all banned names into a HashSet with normalized names
+ HashSet bannedNames = new HashSet(
+ File.ReadLines(BetterDataManager.banNameListFile)
+ .Select(normalizeName)
+ .Where(name => !string.IsNullOrWhiteSpace(name))
+ );
+
+ string normalizedPlayerName = normalizeName(player.Data.PlayerName);
+
+ // Check if any banned name is a prefix of the player's normalized name
+ bool isNameBanned = bannedNames.Any(bannedName =>
+ normalizedPlayerName.StartsWith(bannedName)
+ );
+
+ if (!string.IsNullOrEmpty(normalizedPlayerName) && isNameBanned)
+ {
+ player.Kick(false, $"{player.Data.PlayerName} has been kicked due to their name being on the ban name list!");
+ }
+
+ }
}
}, 2.5f, "OnPlayerJoinedPatch", false);
}
@@ -79,11 +103,23 @@ class OnPlayerLeftPatch
HudManager.Instance?.NotifyOfDisconnect(data.Character);
}
}
+
+[HarmonyPatch(typeof(GameData))]
+[HarmonyPatch("HandleDisconnect")]
+[HarmonyPatch(MethodType.Normal)]
+[HarmonyPatch(new Type[] { typeof(PlayerControl), typeof(DisconnectReasons) })]
+class GameDataHandleDisconnectPatch
+{
+ public static void Prefix(/*GameData __instance,*/ [HarmonyArgument(0)] PlayerControl player, [HarmonyArgument(1)] DisconnectReasons reason)
+ {
+ player.BetterData().DisconnectReason = reason;
+ }
+}
[HarmonyPatch(typeof(GameData), nameof(GameData.ShowNotification))]
class GameDataShowNotificationPatch
{
- public static bool Prefix(AmongUsClient __instance, ref string playerName, ref DisconnectReasons reason)
+ public static bool Prefix(/*GameData __instance,*/ ref string playerName, ref DisconnectReasons reason)
{
string ReasonText;
switch (reason)
diff --git a/Patches/RoleManagerPatch.cs b/Patches/RoleManagerPatch.cs
index 860a07d..3f65851 100644
--- a/Patches/RoleManagerPatch.cs
+++ b/Patches/RoleManagerPatch.cs
@@ -23,6 +23,20 @@ public class RoleManagerPatch
{
if (!Main.BetterRoleAlgorithma.Value) return true;
+ if (!GameStates.IsHideNSeek)
+ {
+ RegularBetterRoleAssignment();
+ }
+ else
+ {
+ HideAndSeekBetterRoleAssignment();
+ }
+
+ return false;
+ }
+
+ public static void RegularBetterRoleAssignment()
+ {
Logger.LogHeader($"Better Role Assignment Has Started", "RoleManager");
// Set roles up
@@ -245,8 +259,118 @@ public class RoleManagerPatch
}, 1f, "RoleManager SyncAllNames");
Logger.LogHeader($"Better Role Assignment Has Finished", "RoleManager");
+ }
- return false;
+ public static void HideAndSeekBetterRoleAssignment()
+ {
+ Logger.LogHeader($"Better Role Assignment Has Started", "RoleManager");
+
+ foreach (var addplayer in Main.AllPlayerControls.Where(pc => !ImpostorMultiplier.ContainsKey(Utils.GetHashPuid(pc))))
+ ImpostorMultiplier[Utils.GetHashPuid(addplayer)] = 0;
+
+ int NumImpostors = 1;
+
+ int NumPlayers = Main.AllPlayerControls.Length;
+
+ List Impostors = [];
+ List Crewmates = [];
+
+ // Set imp from settings
+ int SetImpostor = GameOptionsManager.Instance.currentHideNSeekGameOptions.ImpostorPlayerID;
+
+ if (SetImpostor >= 0)
+ {
+ var player = Utils.PlayerFromId(SetImpostor);
+ if (player != null)
+ {
+ Impostors.Add(player);
+ player.RpcSetRole(RoleTypes.Impostor);
+ player.roleAssigned = true;
+ Logger.Log($"Settings Assigned {Utils.GetRoleName(RoleTypes.Impostor)} role to {player.Data.PlayerName}", "RoleManager");
+ }
+ }
+
+ // Override player role assignment
+ if (SetPlayerRole.Keys.Any())
+ {
+ foreach (var kvp in SetPlayerRole.Where(kvp => kvp.Key != null).OrderBy(kvp => kvp.Key == PlayerControl.LocalPlayer ? 0 : 1))
+ {
+ var player = kvp.Key;
+ var role = kvp.Value;
+
+ if (role is RoleTypes.Impostor)
+ {
+ if (Impostors.Count < NumImpostors)
+ {
+ Impostors.Add(player);
+ player.RpcSetRole(RoleTypes.Impostor);
+ player.roleAssigned = true;
+ Logger.Log($"Override Assigned {Utils.GetRoleName(RoleTypes.Impostor)} role to {player.Data.PlayerName}", "RoleManager");
+ }
+ else continue;
+ }
+ else if (role is RoleTypes.Engineer)
+ {
+ Crewmates.Add(player);
+ player.RpcSetRole(RoleTypes.Engineer);
+ player.roleAssigned = true;
+ Logger.Log($"Override Assigned {Utils.GetRoleName(RoleTypes.Engineer)} role to {player.Data.PlayerName}", "RoleManager");
+ }
+ }
+ }
+
+ // Get players in random order
+ List players = Main.AllPlayerControls
+ .Where(player => !Impostors.Contains(player) && !Crewmates.Contains(player) && player.roleAssigned == false)
+ .ToList();
+
+ int n = players.Count;
+ while (n > 1)
+ {
+ n--;
+ int k = random.Next(n + 1);
+ PlayerControl value = players[k];
+ players[k] = players[n];
+ players[n] = value;
+ }
+
+ // Assign roles
+ foreach (PlayerControl pc in players)
+ {
+ if (pc == null || pc.roleAssigned == true) continue;
+
+ if (Impostors.Count < NumImpostors && RNG() > ImpostorMultiplier[Utils.GetHashPuid(pc)])
+ {
+ if (!Impostors.Contains(pc))
+ {
+ ImpostorMultiplier[Utils.GetHashPuid(pc)] += 15;
+ Impostors.Add(pc);
+ pc.RpcSetRole(RoleTypes.Impostor);
+ pc.roleAssigned = true;
+ Logger.Log($"Assigned {Utils.GetRoleName(RoleTypes.Impostor)} role to {pc.Data.PlayerName}", "RoleManager");
+ }
+ }
+ else
+ {
+ if (!Crewmates.Contains(pc))
+ {
+ ImpostorMultiplier[Utils.GetHashPuid(pc)] = 0;
+ Crewmates.Add(pc);
+ pc.RpcSetRole(RoleTypes.Engineer);
+ pc.roleAssigned = true;
+ Logger.Log($"Assigned {Utils.GetRoleName(RoleTypes.Engineer)} role to {pc.Data.PlayerName}", "RoleManager");
+ }
+ }
+ }
+
+ SetPlayerRole.Clear();
+
+ _ = new LateTask(() =>
+ {
+ RPC.SyncAllNames(false, true, Main.BetterHost.Value);
+ }, 1f, "RoleManager SyncAllNames");
+
+ Logger.LogHeader($"Better Role Assignment Has Finished", "RoleManager");
}
[HarmonyPatch(nameof(RoleManager.AssignRoleOnDeath))]
@@ -254,7 +378,7 @@ public class RoleManagerPatch
public static bool AssignRoleOnDeath_Prefix(/*RoleManager __instance*/ [HarmonyArgument(0)] PlayerControl player)
{
if (player.Data.RoleType != RoleTypes.CrewmateGhost && player.Data.RoleType != RoleTypes.ImpostorGhost)
- player.BetterData().DeadDisplayRole = player.Data.RoleType;
+ player.BetterData().RoleInfo.DeadDisplayRole = player.Data.RoleType;
if (!Main.BetterRoleAlgorithma.Value) return true;
@@ -285,6 +409,8 @@ public class RoleManagerPatch
foreach (var kvp in ghostRoles)
{
+ if (player.IsImpostorTeam() && kvp.Key is RoleTypes.GuardianAngel) continue;
+
if (kvp.Value > 0 && RNG() <= GameOptionsManager.Instance.CurrentGameOptions.RoleOptions.GetChancePerGame(kvp.Key))
{
player.RpcSetRole(kvp.Key);
diff --git a/Patches/RolePatch.cs b/Patches/RolePatch.cs
index ecfd18e..764ff7f 100644
--- a/Patches/RolePatch.cs
+++ b/Patches/RolePatch.cs
@@ -1,5 +1,4 @@
using HarmonyLib;
-using Rewired;
namespace BetterAmongUs.Patches;
@@ -12,12 +11,12 @@ class RolePatch
[HarmonyPrefix]
public static bool NotifyOfDeath_Prefix(NoisemakerRole __instance)
{
- if (__instance.Player.BetterData().HasNoisemakerNotify)
+ if (__instance.Player.BetterData().RoleInfo.HasNoisemakerNotify)
{
return false;
}
- __instance.Player.BetterData().HasNoisemakerNotify = true;
+ __instance.Player.BetterData().RoleInfo.HasNoisemakerNotify = true;
return true;
}
diff --git a/main.cs b/main.cs
index 24d4de3..559573c 100644
--- a/main.cs
+++ b/main.cs
@@ -28,7 +28,7 @@ public class Main : BasePlugin
public const bool IsHotFix = false;
public const string PluginGuid = "com.d1gq.betteramongus";
public const string PluginVersion = "1.0.2";
- public const string ReleaseDate = "08.15.2024"; // mm/dd/yyyy
+ public const string ReleaseDate = "08.16.2024"; // mm/dd/yyyy
public const string Github = "https://github.com/D1GQ/BetterAmongUs-Public";
public const string Discord = "https://discord.gg/vjYrXpzNAn";
@@ -125,16 +125,16 @@ public class Main : BasePlugin
Harmony.PatchAll();
BetterDataManager.SetUp();
- BetterDataManager.LoadCheatData();
+ BetterDataManager.LoadData();
LoadOptions();
if (File.Exists(Path.Combine($"{Environment.CurrentDirectory}/Among Us_Data/Plugins/x86", "steam_api.dll")))
File.WriteAllText(Path.Combine(Environment.CurrentDirectory, "steam_appid.txt"), "945360");
- if (File.Exists(Path.Combine(Environment.CurrentDirectory, "better-log.txt")))
- File.WriteAllText(Path.Combine(Environment.CurrentDirectory, "better-previous-log.txt"), File.ReadAllText(Path.Combine(Environment.CurrentDirectory, "better-log.txt")));
+ if (File.Exists(Path.Combine(BetterDataManager.filePathFolder, "better-log.txt")))
+ File.WriteAllText(Path.Combine(BetterDataManager.filePathFolder, "better-previous-log.txt"), File.ReadAllText(Path.Combine(BetterDataManager.filePathFolder, "better-log.txt")));
- File.WriteAllText(Path.Combine(Environment.CurrentDirectory, "better-log.txt"), "");
+ File.WriteAllText(Path.Combine(BetterDataManager.filePathFolder, "better-log.txt"), "");
BetterAmongUs.Logger.Log("Better Among Us successfully loaded!");
// Set up debug menu