Better desync method

This commit is contained in:
D1GQ committed 2026-02-26 20:33:27 -06:00
1 parent 31dfe39440
commit d9d2f80afe
3 files changed
+56 -67

No files matched your search

+26 -43
View File
@@ -178,57 +178,40 @@ internal static class InnerNetClientHelper
}
/// <summary>
/// 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.
/// </summary>
/// <param name="client">The InnerNetClient instance.</param>
/// <param name="playerNetId">The network ID of the player.</param>
/// <param name="callId">The RPC call ID.</param>
/// <param name="option">The send option for the RPC.</param>
/// <param name="ignoreClientId">The client ID to ignore. Default is -1, which means no client is ignored.</param>
/// <param name="clientCheck">Optional function to filter which clients receive the RPC.</param>
/// <returns>A list of MessageWriter instances for the RPC calls.</returns>
/// <example>
/// <code>
/// List&lt;MessageWriter&gt; messageWriter = AmongUsClient.Instance.StartRpcDesync(PlayerNetId, (byte)RpcCalls, SendOption, ClientId);
/// messageWriter.ForEach(mW => mW.Write("RPC TEST"));
/// AmongUsClient.Instance.FinishRpcDesync(messageWriter);
/// </code>
/// </example>
internal static List<MessageWriter> StartRpcDesync(this InnerNetClient client, uint playerNetId, byte callId, SendOption option, int ignoreClientId = -1, Func<ClientData, bool>? clientCheck = null)
/// <param name="client">The InnerNetClient instance used to send the RPC messages.</param>
/// <param name="playerNetId">The network ID of the player on whose behalf the RPC is sent.</param>
/// <param name="callId">The identifier for the RPC call to be executed.</param>
/// <param name="option">The send option that determines how the message is delivered (e.g., reliability, ordering).</param>
/// <param name="targetClients">A function that selects which clients should receive the RPC message. The RPC is sent only to clients for which
/// this function returns <see langword="true"/>.</param>
/// <param name="writeTo">An action that writes the RPC payload to the provided MessageWriter.</param>
internal static void SendRpcImmediatelyDesync(this InnerNetClient client, uint playerNetId, RpcCalls callId, SendOption option, Func<ClientData, bool> targetClients, Action<MessageWriter> writeTo)
{
List<MessageWriter> 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);
}
}
/// <summary>
/// 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.
/// </summary>
/// <param name="client">The InnerNetClient instance.</param>
/// <param name="messageWriters">The list of MessageWriters to finish and send.</param>
internal static void FinishRpcDesync(this InnerNetClient client, List<MessageWriter> messageWriters)
/// <param name="client">The InnerNetClient instance used to send the RPC message.</param>
/// <param name="playerNetId">The network identifier of the target player to whom the RPC is sent.</param>
/// <param name="callId">The identifier of the RPC call to invoke.</param>
/// <param name="option">The send option that determines how the message is delivered (such as reliability or channel).</param>
/// <param name="writeTo">An action that writes the RPC message content to the provided MessageWriter.</param>
/// <param name="targetClientId">The client identifier of the specific target client. Use -1 to broadcast to all clients.</param>
internal static void SendRpcImmediately(this InnerNetClient client, uint playerNetId, RpcCalls callId, SendOption option, Action<MessageWriter> 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);
}
}
+8 -8
View File
@@ -29,15 +29,15 @@ internal static class RPC
/// <param name="targetClientId">The specific client ID to target, or -1 to broadcast to all clients.</param>
internal static void SendCustomRpcPacked(CustomRPC customRPC, Action<MessageWriter> 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);
}
/// <summary>
@@ -15,11 +15,14 @@ internal static class RoleManagerPatch
internal static Dictionary<string, int> ImpostorMultiplier = []; // HashPuid, Multiplier
private static readonly Random random = new();
// Check if client is not verified Better Among Us user
static readonly Func<InnerNet.ClientData, bool> clientCheck = (clientData) =>
// Check if client is verified Better Among Us user
private static Func<InnerNet.ClientData, bool> 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> 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> 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> 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;