mirror of
https://github.com/D1GQ/BetterAmongUs.git
synced 2026-10-20 01:46:19 +00:00
Allow host only mods to send Support Flags
This commit is contained in:
1 parent
8107cd4462
commit
744010b82d
9 files changed
+178
-29
No files matched your search
@@ -0,0 +1,64 @@
|
||||
using HarmonyLib;
|
||||
using Hazel;
|
||||
|
||||
namespace BetterAmongUs.Examples;
|
||||
|
||||
/// <summary>
|
||||
/// Example patch that demonstrates how to send BetterAmongUs modded support flags to BAU users.
|
||||
/// This patch hooks into the player info serialization process to transmit custom flags that control various BAU features.
|
||||
/// </summary>
|
||||
[HarmonyPatch]
|
||||
internal static class SendBAUSupportFlagsExamplePatch
|
||||
{
|
||||
/// <summary>
|
||||
/// Postfix patch that runs after NetworkedPlayerInfo.Serialize.
|
||||
/// When the host is serializing the player data, this method writes
|
||||
/// BAU support flags into the message stream to communicate mod capabilities to the BAU client.
|
||||
/// </summary>
|
||||
/// <param name="__instance">The NetworkedPlayerInfo instance being serialized.</param>
|
||||
/// <param name="writer">The MessageWriter used for serialization.</param>
|
||||
/// <remarks>
|
||||
/// This example sends the "command.force.bau.prefix" flag which forces all BAU commands
|
||||
/// to use the "bau:" prefix. The flag is hashed using GetFlagHash for efficient transmission.
|
||||
/// <para>
|
||||
/// <b>Important:</b> This functionality only works on protocol version +25 (modded protocol)!
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[HarmonyPatch(typeof(NetworkedPlayerInfo), nameof(NetworkedPlayerInfo.Serialize))]
|
||||
[HarmonyPriority(Priority.Last)]
|
||||
[HarmonyPostfix]
|
||||
internal static void Serialize_Postfix(NetworkedPlayerInfo __instance, MessageWriter writer)
|
||||
{
|
||||
if (!AmongUsClient.Instance.AmHost || __instance.OwnerId != -2)
|
||||
return;
|
||||
|
||||
writer.Write("bau:flags"); // Mark message as BAU flags
|
||||
writer.WritePacked(1); // Amount of flags we are sending
|
||||
writer.WritePacked(GetFlagHash("command.force.bau.prefix")); // Send flag hash, most use GetFlagHash with the exact logic in this example!
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a deterministic integer hash from a string for use as a flag identifier.
|
||||
/// </summary>
|
||||
/// <param name="input">The input string to hash</param>
|
||||
/// <returns>
|
||||
/// A 32-bit integer hash value. Returns 0 for null or empty strings.
|
||||
/// </returns>
|
||||
internal static int GetFlagHash(string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input)) return 0;
|
||||
|
||||
int hash = 17;
|
||||
hash = hash * 31 + input.Length;
|
||||
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
char c = input[i];
|
||||
hash = hash * 31 + c;
|
||||
hash = hash * 31 + i;
|
||||
}
|
||||
|
||||
hash = hash * 31 + input.Length;
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ using HarmonyLib;
|
||||
using Il2CppInterop.Runtime.Injection;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace BetterAmongUs;
|
||||
|
||||
@@ -160,6 +161,7 @@ internal class BAUPlugin : BasePlugin
|
||||
BAUModdedSupportEvents.OnBAUOptionsLoadedEvent.InvokeAll([.. OptionItem.AllOptions.Cast<object>()]);
|
||||
InstanceAttribute.RegisterAll();
|
||||
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));
|
||||
@@ -171,6 +173,17 @@ internal class BAUPlugin : BasePlugin
|
||||
Logger_.Log($"BetterAmongUs {BetterAmongUsVersion}-{ModInfo.BuildDate} - [{AppVersion} --> {SupportedVersions}] {Utils.GetPlatformName(PlatformData.Platform)}");
|
||||
}
|
||||
|
||||
private static void OnSceneLoaded(Scene scene, LoadSceneMode _)
|
||||
{
|
||||
if (AmongUsClient.Instance != null)
|
||||
{
|
||||
if (scene.name == AmongUsClient.Instance.MainMenuScene)
|
||||
{
|
||||
BAUModdedSupportFlags.ClearTempFlags();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up the console window for logging.
|
||||
/// </summary>
|
||||
|
||||
@@ -108,5 +108,7 @@ internal static class ModInfo
|
||||
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";
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ internal sealed class HandshakeHandler
|
||||
{
|
||||
if (!BAUConfigs.SendBetterRpc.Value) yield break;
|
||||
|
||||
while (_extendedData._Data == null || _extendedData._Data.Object == null || PlayerControl.LocalPlayer == null)
|
||||
while (_extendedData.BaseMono == null || _extendedData.BaseMono.Object == null || PlayerControl.LocalPlayer == null)
|
||||
{
|
||||
if (GameState.IsFreePlay) yield break;
|
||||
yield return null;
|
||||
@@ -71,7 +71,7 @@ internal sealed class HandshakeHandler
|
||||
// Local client sends to client
|
||||
private void SendSecretToPlayer()
|
||||
{
|
||||
if (_extendedData._Data.Object.IsLocalPlayer())
|
||||
if (_extendedData.BaseMono.Object.IsLocalPlayer())
|
||||
return;
|
||||
|
||||
if (HasSendSharedSecret)
|
||||
@@ -84,7 +84,7 @@ internal sealed class HandshakeHandler
|
||||
writer.Write(SharedSecret.CryptoAvailable);
|
||||
writer.WriteBytes(SharedSecret.GetPublicKey());
|
||||
writer.Write(SharedSecret.GetTempKey());
|
||||
}, _extendedData._Data.ClientId);
|
||||
}, _extendedData.BaseMono.ClientId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -94,7 +94,7 @@ internal sealed class HandshakeHandler
|
||||
// Client receives from local client
|
||||
internal void HandleSecretFromSender(MessageReader reader)
|
||||
{
|
||||
if (_extendedData._Data?.Object?.IsLocalPlayer() == true)
|
||||
if (_extendedData.BaseMono?.Object?.IsLocalPlayer() == true)
|
||||
return;
|
||||
|
||||
bool senderSupportsCrypto = reader.ReadBoolean();
|
||||
@@ -116,7 +116,7 @@ internal sealed class HandshakeHandler
|
||||
_extendedData.IsBetterUser = true;
|
||||
|
||||
TryHandlePendingVerificationData();
|
||||
SendSecretHashToSender(tempKey, _extendedData._Data.ClientId);
|
||||
SendSecretHashToSender(tempKey, _extendedData.BaseMono.ClientId);
|
||||
ResendSecretToPlayer();
|
||||
}
|
||||
|
||||
|
||||
@@ -221,6 +221,7 @@ public static class BAUModdedSupportFlags
|
||||
public static string Disable_CustomColorBlindText = "gameplay.disable.customcolorblindtext";
|
||||
|
||||
private static readonly HashSet<string> _flags = [];
|
||||
private static readonly HashSet<int> _tempFlags = [];
|
||||
private static bool _initialized = false;
|
||||
|
||||
private static readonly BAUSupportVar<string[]> BAUSupportFlags = new("bau:flags");
|
||||
@@ -262,6 +263,23 @@ public static class BAUModdedSupportFlags
|
||||
_flags.Add(flag);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manually adds a temporary flag to the internal temporary flag collection.
|
||||
/// </summary>
|
||||
/// <param name="flag">The flag hash to add to the collection.</param>
|
||||
internal static void AddTempFlag(int flag)
|
||||
{
|
||||
_tempFlags.Add(flag);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CLears all temporary flags.
|
||||
/// </summary>
|
||||
internal static void ClearTempFlags()
|
||||
{
|
||||
_tempFlags.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a specific flag has been declared by any loaded mod.
|
||||
/// </summary>
|
||||
@@ -269,6 +287,31 @@ public static class BAUModdedSupportFlags
|
||||
/// <returns><c>true</c> if the flag is present; otherwise, <c>false</c>.</returns>
|
||||
public static bool HasFlag(string flag)
|
||||
{
|
||||
return _flags.Contains(flag);
|
||||
return _flags.Contains(flag) || _tempFlags.Contains(GetFlagHash(flag));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a integer hash from a string for use as a flag identifier.
|
||||
/// </summary>
|
||||
/// <param name="input">The input string to hash. Can be null or empty.</param>
|
||||
/// <returns>
|
||||
/// A integer hash value. Returns 0 for null or empty strings.
|
||||
/// </returns>
|
||||
internal static int GetFlagHash(string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input)) return 0;
|
||||
|
||||
int hash = 17;
|
||||
hash = hash * 31 + input.Length;
|
||||
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
char c = input[i];
|
||||
hash = hash * 31 + c;
|
||||
hash = hash * 31 + i;
|
||||
}
|
||||
|
||||
hash = hash * 31 + input.Length;
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
@@ -17,18 +17,13 @@ internal sealed class ExtendedPlayerControl : MonoBehaviour, IMonoExtension<Play
|
||||
/// </summary>
|
||||
public PlayerControl? BaseMono { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the PlayerControl instance.
|
||||
/// </summary>
|
||||
internal PlayerControl? _Player => BaseMono;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (!this.RegisterExtension())
|
||||
return;
|
||||
|
||||
this.StartCoroutine(CoAddBetterData());
|
||||
_Player.gameObject.AddComponent<PlayerInfoDisplay>().Init(_Player);
|
||||
BaseMono.gameObject.AddComponent<PlayerInfoDisplay>().Init(BaseMono);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -38,12 +33,12 @@ internal sealed class ExtendedPlayerControl : MonoBehaviour, IMonoExtension<Play
|
||||
[HideFromIl2Cpp]
|
||||
private IEnumerator CoAddBetterData()
|
||||
{
|
||||
while (_Player == null || _Player.Data == null)
|
||||
while (BaseMono == null || BaseMono.Data == null)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
TryCreateExtendedData(_Player.Data);
|
||||
TryCreateExtendedData(BaseMono.Data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
using BetterAmongUs.Helpers;
|
||||
using BetterAmongUs.Managers;
|
||||
using BetterAmongUs.Modules;
|
||||
using BetterAmongUs.Modules.Support;
|
||||
using HarmonyLib;
|
||||
using Hazel;
|
||||
using Il2CppInterop.Runtime.Attributes;
|
||||
using InnerNet;
|
||||
using UnityEngine;
|
||||
@@ -21,15 +24,13 @@ internal sealed class ExtendedPlayerInfo : MonoBehaviour, IMonoExtension<Network
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger_.Error("Handshake disabled: " + ex.Message);
|
||||
Logger_.Log("Handshake disabled: " + ex.Message);
|
||||
HandshakeHandler = null;
|
||||
}
|
||||
}
|
||||
|
||||
public NetworkedPlayerInfo? BaseMono { get; set; }
|
||||
|
||||
internal NetworkedPlayerInfo? _Data => BaseMono;
|
||||
|
||||
private bool hasSet = false;
|
||||
|
||||
/// <summary>
|
||||
@@ -75,17 +76,17 @@ internal sealed class ExtendedPlayerInfo : MonoBehaviour, IMonoExtension<Network
|
||||
|
||||
if (AntiCheatInfo.RPCSentPS > 0)
|
||||
{
|
||||
bool flag = _Data.IsCheater();
|
||||
bool flag = BaseMono.IsCheater();
|
||||
|
||||
if (AntiCheatInfo.RPCSentPS >= ExtendedAntiCheatInfo.MAX_RPC_SENT && !flag)
|
||||
{
|
||||
BetterNotificationManager.NotifyCheat(
|
||||
_Data.Object,
|
||||
BaseMono.Object,
|
||||
Translator.GetString("AntiCheat.Reason.RPCSentPS"),
|
||||
Translator.GetString("AntiCheat.UnauthorizedAction")
|
||||
);
|
||||
|
||||
Logger_.LogCheat($"{_Data.Object.ExtendedData().RealName} {AntiCheatInfo.RPCSentPS} Sent.");
|
||||
Logger_.LogCheat($"{BaseMono.Object.ExtendedData().RealName} {AntiCheatInfo.RPCSentPS} Sent.");
|
||||
}
|
||||
|
||||
timeAccumulator += time;
|
||||
@@ -98,6 +99,28 @@ internal sealed class ExtendedPlayerInfo : MonoBehaviour, IMonoExtension<Network
|
||||
}
|
||||
}
|
||||
|
||||
internal void Deserialize(MessageReader reader)
|
||||
{
|
||||
if (BaseMono.OwnerId != -2)
|
||||
return;
|
||||
|
||||
if (reader.BytesRemaining > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (reader.ReadString() == ModInfo.Constants.BAU_MODDED_PROTOCOL_FLAG)
|
||||
{
|
||||
int flagCount = reader.ReadPackedInt32();
|
||||
for (int i = 0; i < flagCount; i++)
|
||||
{
|
||||
BAUModdedSupportFlags.AddTempFlag(reader.ReadPackedInt32());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the handshake handler for this player.
|
||||
/// </summary>
|
||||
@@ -113,7 +136,7 @@ internal sealed class ExtendedPlayerInfo : MonoBehaviour, IMonoExtension<Network
|
||||
/// <summary>
|
||||
/// Gets the player's real name.
|
||||
/// </summary>
|
||||
internal string RealName => _Data?.PlayerName ?? "???";
|
||||
internal string RealName => BaseMono?.PlayerName ?? "???";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the last name set for this player.
|
||||
@@ -245,6 +268,18 @@ internal sealed class ExtendedRoleInfo
|
||||
/// </summary>
|
||||
internal static class PlayerControlDataExtension
|
||||
{
|
||||
[HarmonyPatch(typeof(NetworkedPlayerInfo))]
|
||||
class NetworkedPlayerInfoPatch
|
||||
{
|
||||
[HarmonyPatch(nameof(NetworkedPlayerInfo.Deserialize))]
|
||||
[HarmonyPriority(Priority.Last)]
|
||||
[HarmonyPostfix]
|
||||
internal static void Deserialize_Postfix(NetworkedPlayerInfo __instance, MessageReader reader)
|
||||
{
|
||||
__instance.ExtendedData()?.Deserialize(reader);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets extended player data from a PlayerControl.
|
||||
/// </summary>
|
||||
|
||||
+2
-6
@@ -15,10 +15,6 @@ internal static class RPC
|
||||
/// </summary>
|
||||
internal const RpcCalls CUSTOM_RPC_CALL = RpcCalls.CancelPet;
|
||||
|
||||
/// <summary>
|
||||
/// The flag string used to identify custom RPC messages packed within vanilla RPC calls.
|
||||
/// </summary>
|
||||
internal const string CUSTOM_RPC_FLAG = "bau:rpc";
|
||||
|
||||
/// <summary>
|
||||
/// Sends a custom RPC message packed within a vanilla SetNamePlateStr RPC call.
|
||||
@@ -32,7 +28,7 @@ internal static class RPC
|
||||
{
|
||||
AmongUsClient.Instance.SendRpcImmediately(PlayerControl.LocalPlayer.NetId, CUSTOM_RPC_CALL, SendOption.Reliable, writer =>
|
||||
{
|
||||
writer.Write(CUSTOM_RPC_FLAG); // Flag to check if its a rpc packed into SetNamePlateStr
|
||||
writer.Write(ModInfo.Constants.BAU_CUSTOM_RPC_FLAG); // Flag to check if its a rpc packed into SetNamePlateStr
|
||||
writer.Write((byte)customRPC);
|
||||
action(writer);
|
||||
}, targetClientId);
|
||||
@@ -120,7 +116,7 @@ internal static class RPC
|
||||
{
|
||||
try
|
||||
{
|
||||
if (reader.ReadString() == CUSTOM_RPC_FLAG)
|
||||
if (reader.ReadString() == ModInfo.Constants.BAU_CUSTOM_RPC_FLAG)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ internal static class ClientPatch
|
||||
|
||||
[HarmonyPatch(typeof(SignInStatusComponent), nameof(SignInStatusComponent.SetOnline))]
|
||||
[HarmonyPrefix]
|
||||
private static bool SignInStatusComponent_SetOnline_Prefix(SignInStatusComponent __instance)
|
||||
private static bool SignInStatusComponent_SetOnline_Prefix()
|
||||
{
|
||||
// Get supported Among Us versions for BAU
|
||||
var varSupportedVersions = ModInfo.SupportedAmongUsVersions;
|
||||
@@ -80,10 +80,11 @@ internal static class ClientPatch
|
||||
|
||||
[HarmonyPatch(typeof(AmongUsClient), nameof(AmongUsClient.ExitGame))]
|
||||
[HarmonyPostfix]
|
||||
private static void AmongUsClient_ExitGame_Postfix([HarmonyArgument(0)] DisconnectReasons reason)
|
||||
private static void AmongUsClient_ExitGame_Postfix(DisconnectReasons reason)
|
||||
{
|
||||
// Hide custom loading bar when exiting game
|
||||
CustomLoadingBarManager.ToggleLoadingBar(false);
|
||||
|
||||
Logger_.Log($"Client has left game for: {Enum.GetName(reason)}", "AmongUsClientPatch");
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user