add DisableSabotagesForDead and remove private only lobbies

This commit is contained in:
D1GQ committed 2026-03-18 21:41:51 -05:00
1 parent 919a82648f
commit 13a5564c04
8 files changed
+87 -191

No files matched your search

+4 -9
View File
@@ -73,11 +73,6 @@ internal static class BAUConfigs
/// </summary>
internal static BAUConfigEntry<string> CommandPrefix { get; } = new("Mod", "CommandPrefix", "/");
/// <summary>
/// Gets the configuration entry for private only lobby setting.
/// </summary>
internal static BAUConfigEntry<bool> PrivateOnlyLobby { get; } = new("Mod", "PrivateOnlyLobby", false);
/// <summary>
/// Gets the configuration entry for favorite color setting.
/// </summary>
@@ -94,10 +89,10 @@ internal static class BAUConfigs
internal static void LoadConfigs()
{
BAUModdedSupportEvents.InvokeAll_OnBAUConfigEntriesLoaded([
PrivateOnlyLobby, AntiCheat, SendBetterRpc,
BetterNotifications, ForceOwnLanguage, ChatDarkMode,
ChatInGameplay, LobbyPlayerInfo, DisableLobbyTheme,
UnlockFPS, ShowFPS, MinimapIcons, VentColorGroups, CommandPrefix,
AntiCheat, SendBetterRpc, BetterNotifications,
ForceOwnLanguage, ChatDarkMode, ChatInGameplay, LobbyPlayerInfo,
DisableLobbyTheme, UnlockFPS, ShowFPS,
MinimapIcons, VentColorGroups, CommandPrefix,
FavoriteColor, SettingsPreset
]);
+37 -4
View File
@@ -462,7 +462,18 @@ internal static class NetworkManager
BetterAntiCheat.HandleCheatRPCBeforeCheck(player, callId, reader);
if (BetterAntiCheat.CheckCancelRPC(player, callId, reader) != true)
if (GameState.IsHost)
{
var tempReader = MessageReader.Get(reader);
if (RPCHandler.HandleRPC(callId, player, tempReader, HandlerFlag.BetterHost) == false)
{
tempReader.Recycle();
return false;
}
tempReader.Recycle();
}
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}");
return false;
@@ -474,10 +485,32 @@ internal static class NetworkManager
return true;
}
[HarmonyPatch(typeof(ShipStatus), nameof(ShipStatus.UpdateSystem), typeof(SystemTypes), typeof(PlayerControl), typeof(MessageReader))]
[HarmonyPatch]
internal static class MessageReaderUpdateSystemPatch
{
internal static bool Prefix([HarmonyArgument(0)] SystemTypes systemType, [HarmonyArgument(1)] PlayerControl player, [HarmonyArgument(2)] MessageReader reader)
[HarmonyPatch(typeof(ShipStatus), nameof(ShipStatus.UpdateSystem), typeof(SystemTypes), typeof(PlayerControl), typeof(byte))]
[HarmonyPrefix]
internal static bool ShipStatus_UpdateSystemLocal_Prefix(SystemTypes systemType, PlayerControl player, byte amount)
{
MessageWriter messageWriter = MessageWriter.Get(0);
messageWriter.Write(amount);
MessageReader messageReader = MessageReader.Get(messageWriter.ToByteArray(false));
messageWriter.Recycle();
if (BetterAntiCheat.RpcUpdateSystemCheck(player, systemType, messageReader) != true)
{
messageReader.Recycle();
return false;
}
messageReader.Recycle();
return true;
}
[HarmonyPatch(typeof(ShipStatus), nameof(ShipStatus.UpdateSystem), typeof(SystemTypes), typeof(PlayerControl), typeof(MessageReader))]
[HarmonyPrefix]
internal static bool ShipStatus_UpdateSystem_Prefix(SystemTypes systemType, PlayerControl player, MessageReader msgReader)
{
player.BetterData().AntiCheatInfo.RPCSentPS++;
if (player.BetterData().AntiCheatInfo.RPCSentPS >= ExtendedAntiCheatInfo.MAX_RPC_SENT)
@@ -485,7 +518,7 @@ internal static class NetworkManager
return false;
}
if (BetterAntiCheat.RpcUpdateSystemCheck(player, systemType, reader) != true) return false;
if (BetterAntiCheat.RpcUpdateSystemCheck(player, systemType, msgReader) != true) return false;
return true;
}
+13 -13
View File
@@ -266,24 +266,24 @@ internal static class BetterAntiCheat
/// <returns>True if the sabotage should be allowed, false otherwise.</returns>
internal static bool RpcUpdateSystemCheck(PlayerControl player, SystemTypes systemType, MessageReader oldReader)
{
if (Utils.SystemTypeIsSabotage(systemType) || systemType is SystemTypes.Doors)
{
if (GameState.IsPrivateOnlyLobby && BetterGameSettings.DisableSabotages.GetBool()) return false;
}
MessageReader reader = MessageReader.Get(oldReader);
RegisterRPCHandlerAttribute.GetClassInstance<UpdateSystemHandler>().CatchedSystemType = systemType;
bool notCanceled = RPCHandler.HandleRPC((byte)RpcCalls.UpdateSystem, player, reader, HandlerFlag.AntiCheatCancel);
if (!notCanceled)
if (!RPCHandler.HandleRPC((byte)RpcCalls.UpdateSystem, player, reader, HandlerFlag.AntiCheatCancel))
{
var tempReader = MessageReader.Get(reader);
Logger_.LogCheat($"RPC canceled by Anti-Cheat: {Enum.GetName(typeof(SystemTypes), (int)systemType)} - {tempReader.ReadByte()}");
tempReader.Recycle();
reader.Recycle();
return false;
}
reader.Recycle();
return notCanceled;
MessageReader reader2 = MessageReader.Get(oldReader);
if (!RPCHandler.HandleRPC((byte)RpcCalls.UpdateSystem, player, reader2, HandlerFlag.BetterHost))
{
reader2.Recycle();
return false;
}
reader2.Recycle();
return true;
}
/// <summary>
@@ -1,5 +1,6 @@
using BetterAmongUs.Attributes;
using BetterAmongUs.Helpers;
using BetterAmongUs.Patches.Gameplay.UI.Settings;
using Hazel;
using UnityEngine;
@@ -51,9 +52,8 @@ internal sealed class UpdateSystemHandler : RPCHandler
internal override bool HandleAntiCheatCancel(PlayerControl? sender, MessageReader reader)
{
if (GameState.IsHost && sender.IsHost()) return true;
if (sender.IsHost()) return true;
MessageReader oldReader = MessageReader.Get(reader);
byte count = reader.ReadByte();
if (ShipStatus.Instance.Systems.TryGetValue(CatchedSystemType, out ISystemType system))
@@ -62,12 +62,31 @@ internal sealed class UpdateSystemHandler : RPCHandler
if (systemHandlers.TryGetValue(systemKey, out var handler))
{
oldReader.Recycle();
return handler.Invoke(sender, system, oldReader, count);
return handler.Invoke(sender, system, reader, count);
}
}
oldReader.Recycle();
return true;
}
internal override bool BetterHandle(PlayerControl? sender, MessageReader reader)
{
// Prevent dead impostors from sabotaging if setting is enabled
if (CatchedSystemType is SystemTypes.Sabotage or SystemTypes.Electrical or SystemTypes.LifeSupp or
SystemTypes.Reactor or SystemTypes.Laboratory or SystemTypes.HeliSabotage or
SystemTypes.Comms or SystemTypes.MushroomMixupSabotage)
{
if (BetterGameSettings.DisableSabotages.GetBool())
return false;
if (BetterGameSettings.DisableSabotagesForDead.GetBool())
{
if (!sender.IsAlive())
{
return false;
}
}
}
return true;
}
-7
View File
@@ -1,5 +1,4 @@
using AmongUs.GameOptions;
using BetterAmongUs.Data.Config;
namespace BetterAmongUs.Modules;
@@ -321,12 +320,6 @@ internal static class GameState
/// </summary>
internal static bool IsHost => AmongUsClient.Instance != null && AmongUsClient.Instance.AmHost;
/// <summary>
/// Gets whether the lobby is private-only (requires specific settings).
/// </summary>
internal static bool IsPrivateOnlyLobby => (BAUConfigs.PrivateOnlyLobby.Value ||
(AmongUsClient.Instance != null && AmongUsClient.Instance.AmLocalHost)) && IsHost;
/// <summary>
/// Gets whether the local player can move.
/// </summary>
-150
View File
@@ -1,150 +0,0 @@
using BetterAmongUs.Data.Config;
using BetterAmongUs.Helpers;
using BetterAmongUs.Modules;
using BetterAmongUs.Modules.Support;
using HarmonyLib;
using TMPro;
using UnityEngine;
namespace BetterAmongUs.Patches.Client;
[HarmonyPatch]
internal static class PrivateLobbyPatch
{
private static GameObject? toggle;
private static readonly List<PassiveButton>? buttons = [];
private static TextMeshPro? toggleText;
[HarmonyPatch(typeof(CreateGameOptions), nameof(CreateGameOptions.Show))]
[HarmonyPostfix]
private static void CreateGameOptions_Show_Postfix(CreateGameOptions __instance)
{
// Check if other mods have disabled private lobby feature
if (BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_PrivateLobby))
{
BAUConfigs.PrivateOnlyLobby.Value = false;
return;
}
// Only create toggle UI once
if (toggle != null)
return;
buttons.Clear();
// Clone April Fools toggle as template for private lobby toggle
toggle = UnityEngine.Object.Instantiate(__instance.AprilFoolsToggle, __instance.contentObjects.First().transform.parent);
if (toggle != null)
{
toggle.name = "PrivateOnlyLobby";
// Get the ON and OFF buttons from the toggle
buttons.Add(toggle.transform.Find("AprilOn").GetComponent<PassiveButton>());
buttons.Add(toggle.transform.Find("AprilOff").GetComponent<PassiveButton>());
if (buttons.Count < 2)
return;
toggle.gameObject.SetActive(true);
// Set up ON button click handler
var onButton = buttons[0];
if (onButton != null)
{
onButton.gameObject.SetActive(false);
onButton.OnClick = new();
onButton.OnClick.AddListener(() => TogglePrivateOnlyLobby(true));
}
// Set up OFF button click handler
var offButton = buttons[1];
if (offButton != null)
{
offButton.gameObject.SetActive(false);
offButton.OnClick = new();
offButton.OnClick.AddListener(() => TogglePrivateOnlyLobby(false));
}
// Position toggle at top-right of create game screen
var aspect = toggle.gameObject.AddComponent<AspectPosition>();
aspect.Alignment = AspectPosition.EdgeAlignments.Top;
aspect.DistanceFromEdge = new Vector3(0.4f, 1.62f, 0);
aspect.AdjustPosition();
// Update toggle text label
var modeText = toggle.transform.Find("BlackSquare/ModeText");
if (modeText != null)
{
var text = modeText.GetComponent<TextMeshPro>();
if (text != null)
{
text.DestroyTextTranslators();
text.text = "Private Only Lobby";
toggleText = text;
}
}
}
// Initialize toggle state from saved configuration
TogglePrivateOnlyLobby(BAUConfigs.PrivateOnlyLobby.Value);
}
private static void TogglePrivateOnlyLobby(bool modeOn)
{
if (buttons.Count < 2)
return;
// Show both buttons first
buttons[0].gameObject.SetActive(true);
buttons[1].gameObject.SetActive(true);
// Deselect both buttons
buttons[0].SelectButton(false);
buttons[1].SelectButton(false);
// Select appropriate button based on mode
if (modeOn)
{
buttons[0].SelectButton(true);
}
else
{
buttons[1].SelectButton(true);
}
// Save setting to configuration
BAUConfigs.PrivateOnlyLobby.Value = modeOn;
}
[HarmonyPatch(typeof(LobbyInfoPane), nameof(LobbyInfoPane.Update))]
[HarmonyPostfix]
private static void LobbyInfoPane_Update_Postfix(LobbyInfoPane __instance)
{
// Enforce private lobby setting if enabled by host
if (BAUConfigs.PrivateOnlyLobby.Value && !GameState.IsLocalGame && GameState.IsHost)
{
// Ensure game stays private
if (AmongUsClient.Instance.IsGamePublic)
{
AmongUsClient.Instance.ChangeGamePublic(false);
}
// Disable and visually indicate that public lobby button is locked
var button = __instance.HostPrivateButton.GetComponent<PassiveButton>();
if (button != null)
{
button.enabled = false;
// Change inactive sprite to cyan color to indicate locked state
var inactive = __instance.HostPrivateButton.transform.Find("Inactive");
if (inactive != null)
{
var sprite = inactive.GetComponent<SpriteRenderer>();
if (sprite != null)
{
sprite.color = new Color(0.35f, 1f, 1f, 1f); // Corrected colour assignment
}
}
}
}
}
}
@@ -23,8 +23,11 @@ internal sealed class BetterGameSettings
internal static OptionIntItem? KickLevelBelow;
internal static OptionCheckboxItem? DetectCheatClients;
internal static OptionCheckboxItem? DetectInvalidRPCs;
internal static OptionStringItem? RoleRandomizer;
internal static OptionCheckboxItem? DesyncRoles;
internal static OptionCheckboxItem? DisableSabotagesForDead;
internal static OptionCheckboxItem? CancelInvalidSabotage;
internal static OptionCheckboxItem? CensorDetectionReason;
internal static OptionCheckboxItem? DisableSabotages;
@@ -92,17 +95,19 @@ internal static class GameSettingsPatch
// Gameplay Settings section
{
// Show only for hosts in private lobbies
if (IsPreload || GameState.IsHost && GameState.IsPrivateOnlyLobby)
// Show only for hosts
if (IsPreload || GameState.IsHost)
{
// Normal game mode settings
if (IsPreload || !GameState.IsHideNSeek)
{
OptionHeaderItem.Create(BetterSettingsTab, "BetterSetting.MainHeader.Gameplay");
BetterGameSettings.DisableSabotages = OptionCheckboxItem.Create(1500, BetterSettingsTab, "BetterSetting.Setting.DisableSabotages", false);
BetterGameSettings.DisableSabotagesForDead = OptionCheckboxItem.Create(1600, BetterSettingsTab, "BetterSetting.Setting.DisableSabotagesForDead", false);
}
// Hide & Seek specific settings with dynamic impostor selection
else if (IsPreload || GameState.IsHideNSeek)
if (IsPreload || GameState.IsHideNSeek)
{
OptionHeaderItem.Create(BetterSettingsTab, "BetterSetting.MainHeader.HideNSeek");
OptionTitleItem.Create(BetterSettingsTab, $"<color={RoleTypes.Impostor.GetRoleHex()}>{Translator.GetString(StringNames.ImpostorsCategory)}</color>");
+1
View File
@@ -95,6 +95,7 @@
"BetterSetting.Setting.DetectInvalidRPCs": "Detect invalid RPCs",
"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",
"BetterSetting.Setting.HideAndSeekImpNum": "# Seekers",