diff --git a/src/Helpers/InnerNetClientHelper.cs b/src/Helpers/InnerNetClientHelper.cs index 3549cc0..ddfafa1 100644 --- a/src/Helpers/InnerNetClientHelper.cs +++ b/src/Helpers/InnerNetClientHelper.cs @@ -178,57 +178,40 @@ internal static class InnerNetClientHelper } /// - /// Starts the RPC desynchronization process for the given player, call ID, and send option. + /// Sends an RPC message immediately to all clients that match the specified target criteria, bypassing standard + /// synchronization mechanisms. /// - /// The InnerNetClient instance. - /// The network ID of the player. - /// The RPC call ID. - /// The send option for the RPC. - /// The client ID to ignore. Default is -1, which means no client is ignored. - /// Optional function to filter which clients receive the RPC. - /// A list of MessageWriter instances for the RPC calls. - /// - /// - /// List<MessageWriter> messageWriter = AmongUsClient.Instance.StartRpcDesync(PlayerNetId, (byte)RpcCalls, SendOption, ClientId); - /// messageWriter.ForEach(mW => mW.Write("RPC TEST")); - /// AmongUsClient.Instance.FinishRpcDesync(messageWriter); - /// - /// - internal static List StartRpcDesync(this InnerNetClient client, uint playerNetId, byte callId, SendOption option, int ignoreClientId = -1, Func? clientCheck = null) + /// The InnerNetClient instance used to send the RPC messages. + /// The network ID of the player on whose behalf the RPC is sent. + /// The identifier for the RPC call to be executed. + /// The send option that determines how the message is delivered (e.g., reliability, ordering). + /// A function that selects which clients should receive the RPC message. The RPC is sent only to clients for which + /// this function returns . + /// An action that writes the RPC payload to the provided MessageWriter. + internal static void SendRpcImmediatelyDesync(this InnerNetClient client, uint playerNetId, RpcCalls callId, SendOption option, Func targetClients, Action writeTo) { - List messageWriters = []; - - if (ignoreClientId < 0) + foreach (var allClients in AmongUsClient.Instance.allClients) { - messageWriters.Add(client.StartRpcImmediately(playerNetId, callId, option, -1)); - } - else - { - foreach (var allClients in AmongUsClient.Instance.allClients.WhereIl2Cpp(c => c.Id != ignoreClientId)) - { - if (clientCheck == null || clientCheck.Invoke(allClients)) - { - messageWriters.Add(client.StartRpcImmediately(playerNetId, callId, option, allClients.Id)); - } - } - } + if (!targetClients(allClients)) continue; - return messageWriters; + client.SendRpcImmediately(playerNetId, callId, option, writeTo, allClients.Id); + } } /// - /// Completes and sends the RPC desynchronization messages. + /// Sends a remote procedure call (RPC) message immediately to the specified player, using the provided message + /// writer action and send option. /// - /// The InnerNetClient instance. - /// The list of MessageWriters to finish and send. - internal static void FinishRpcDesync(this InnerNetClient client, List messageWriters) + /// The InnerNetClient instance used to send the RPC message. + /// The network identifier of the target player to whom the RPC is sent. + /// The identifier of the RPC call to invoke. + /// The send option that determines how the message is delivered (such as reliability or channel). + /// An action that writes the RPC message content to the provided MessageWriter. + /// The client identifier of the specific target client. Use -1 to broadcast to all clients. + internal static void SendRpcImmediately(this InnerNetClient client, uint playerNetId, RpcCalls callId, SendOption option, Action writeTo, int targetClientId = -1) { - foreach (var msg in messageWriters) - { - msg.EndMessage(); - msg.EndMessage(); - client.SendOrDisconnect(msg); - msg.Recycle(); - } + var writer = client.StartRpcImmediately(playerNetId, (byte)callId, option, targetClientId); + writeTo(writer); + client.FinishRpcImmediately(writer); } } \ No newline at end of file diff --git a/src/Network/RPC.cs b/src/Network/RPC.cs index 879866f..fc27b30 100644 --- a/src/Network/RPC.cs +++ b/src/Network/RPC.cs @@ -29,15 +29,15 @@ internal static class RPC /// The specific client ID to target, or -1 to broadcast to all clients. internal static void SendCustomRpcPacked(CustomRPC customRPC, Action action, int targetClientId = -1) { - var writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)RpcCalls.SetNamePlateStr, SendOption.Reliable, targetClientId); + AmongUsClient.Instance.SendRpcImmediately(PlayerControl.LocalPlayer.NetId, RpcCalls.SetNamePlateStr, SendOption.Reliable, writer => + { + writer.Write(DataManager.Player.Customization.NamePlate); + writer.Write(PlayerControl.LocalPlayer.GetNextRpcSequenceId(RpcCalls.SetNamePlateStr)); - writer.Write(DataManager.Player.Customization.NamePlate); - writer.Write(PlayerControl.LocalPlayer.GetNextRpcSequenceId(RpcCalls.SetNamePlateStr)); - - writer.Write(CUSTOM_RPC_FLAG); // Flag to check if its a rpc packed into SetNamePlateStr - writer.Write((byte)customRPC); - action(writer); - AmongUsClient.Instance.FinishRpcImmediately(writer); + writer.Write(CUSTOM_RPC_FLAG); // Flag to check if its a rpc packed into SetNamePlateStr + writer.Write((byte)customRPC); + action(writer); + }, targetClientId); } /// diff --git a/src/Patches/Gameplay/Managers/RoleManagerPatch.cs b/src/Patches/Gameplay/Managers/RoleManagerPatch.cs index 44e6d14..512c2ad 100644 --- a/src/Patches/Gameplay/Managers/RoleManagerPatch.cs +++ b/src/Patches/Gameplay/Managers/RoleManagerPatch.cs @@ -15,11 +15,14 @@ internal static class RoleManagerPatch internal static Dictionary ImpostorMultiplier = []; // HashPuid, Multiplier private static readonly Random random = new(); - // Check if client is not verified Better Among Us user - static readonly Func clientCheck = (clientData) => + // Check if client is verified Better Among Us user + private static Func SendTo(PlayerControl target) { - return clientData?.BetterData()?.IsVerifiedBetterUser != true; - }; + return (clientData) => + { + return clientData.Id != target.GetClientId() && clientData?.BetterData()?.IsVerifiedBetterUser != true; + }; + } [HarmonyPatch(typeof(RoleManager), nameof(RoleManager.SetRole))] [HarmonyPrefix] @@ -156,10 +159,11 @@ internal static class RoleManagerPatch { if (kvp.Key is not (RoleTypes.Phantom or RoleTypes.Viper)) { - List messageWriter = AmongUsClient.Instance.StartRpcDesync(pc.NetId, (byte)RpcCalls.SetRole, SendOption.None, pc.GetClientId(), clientCheck); - messageWriter.ForEach(mW => mW.Write((ushort)RoleTypes.Impostor)); - messageWriter.ForEach(mW => mW.Write(false)); - AmongUsClient.Instance.FinishRpcDesync(messageWriter); + AmongUsClient.Instance.SendRpcImmediatelyDesync(pc.NetId, RpcCalls.SetRole, SendOption.None, SendTo(pc), writer => + { + writer.Write((ushort)RoleTypes.Impostor); + writer.Write(false); + }); } } @@ -195,10 +199,11 @@ internal static class RoleManagerPatch { if (kvp.Key is not RoleTypes.Noisemaker) { - List messageWriter = AmongUsClient.Instance.StartRpcDesync(pc.NetId, (byte)RpcCalls.SetRole, SendOption.None, pc.GetClientId(), clientCheck); - messageWriter.ForEach(mW => mW.Write((ushort)RoleTypes.Crewmate)); - messageWriter.ForEach(mW => mW.Write(false)); - AmongUsClient.Instance.FinishRpcDesync(messageWriter); + AmongUsClient.Instance.SendRpcImmediatelyDesync(pc.NetId, RpcCalls.SetRole, SendOption.None, SendTo(pc), writer => + { + writer.Write((ushort)RoleTypes.Crewmate); + writer.Write(false); + }); } } @@ -352,10 +357,11 @@ internal static class RoleManagerPatch // Desync ghost role to hide it from non-BAU players if (BetterGameSettings.DesyncRoles.GetBool()) { - List messageWriter = AmongUsClient.Instance.StartRpcDesync(player.NetId, (byte)RpcCalls.SetRole, SendOption.None, player.GetClientId(), clientCheck); - messageWriter.ForEach(mW => mW.Write((ushort)player.Data.Role.DefaultGhostRole)); - messageWriter.ForEach(mW => mW.Write(false)); - AmongUsClient.Instance.FinishRpcDesync(messageWriter); + AmongUsClient.Instance.SendRpcImmediatelyDesync(player.NetId, RpcCalls.SetRole, SendOption.None, SendTo(player), writer => + { + writer.Write((ushort)player.Data.Role.DefaultGhostRole); + writer.Write(false); + }); } return false;