mirror of
https://github.com/D1GQ/BetterAmongUs.git
synced 2026-10-20 01:46:19 +00:00
Use different method for custom RPCs due to custom RPCs being blocked soon
This commit is contained in:
1 parent
b739472c21
commit
bc32429f0c
14 files changed
+196
-87
No files matched your search
@@ -2,6 +2,7 @@
|
||||
using Hazel;
|
||||
using InnerNet;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace BetterAmongUs.Helpers;
|
||||
|
||||
@@ -38,6 +39,97 @@ internal static class InnerNetClientHelper
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the next string value in the specified reader matches the provided string without advancing
|
||||
/// the reader's position.
|
||||
/// </summary>
|
||||
/// <param name="reader">The message reader to inspect for the presence of the string. Cannot be null.</param>
|
||||
/// <param name="string">The string to compare against the next value in the reader. If null or empty, the method returns <see
|
||||
/// langword="true"/>.</param>
|
||||
/// <returns>true if the next string value in the reader matches the specified string; otherwise, false.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown if <paramref name="reader"/> is null.</exception>
|
||||
internal static bool HasString(this MessageReader reader, string @string)
|
||||
{
|
||||
if (reader == null) throw new ArgumentNullException(nameof(reader));
|
||||
if (string.IsNullOrEmpty(@string)) return true;
|
||||
|
||||
// Save current position
|
||||
int originalPosition = reader.Position;
|
||||
int originalReadHead = reader.readHead;
|
||||
|
||||
try
|
||||
{
|
||||
// Check if we have enough bytes for at least the packed int length
|
||||
if (reader.BytesRemaining < 1)
|
||||
return false;
|
||||
|
||||
// Try to read the packed int length without modifying internal state
|
||||
int lengthOffset = reader.Position;
|
||||
int lengthReadHead = reader.readHead;
|
||||
|
||||
// Read packed int manually without affecting the reader's state
|
||||
bool readMore = true;
|
||||
int shift = 0;
|
||||
uint stringLength = 0;
|
||||
|
||||
while (readMore)
|
||||
{
|
||||
if (lengthOffset >= reader.Length)
|
||||
return false; // Not enough data
|
||||
|
||||
byte b = reader.Buffer[reader.Offset + lengthOffset];
|
||||
lengthOffset++;
|
||||
|
||||
if (b >= 0x80)
|
||||
{
|
||||
readMore = true;
|
||||
b ^= 0x80;
|
||||
}
|
||||
else
|
||||
{
|
||||
readMore = false;
|
||||
}
|
||||
|
||||
stringLength |= (uint)(b << shift);
|
||||
shift += 7;
|
||||
|
||||
// Sanity check: string length shouldn't be ridiculously large
|
||||
if (stringLength > reader.Buffer.Length || stringLength > 1024 * 1024) // 1MB max
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we have enough bytes for the string content
|
||||
if (reader.Length - lengthOffset < (int)stringLength)
|
||||
return false;
|
||||
|
||||
// Compare the string
|
||||
if (stringLength != @string.Length)
|
||||
return false;
|
||||
|
||||
byte[] stringBytes = Encoding.UTF8.GetBytes(@string);
|
||||
|
||||
// Compare byte by byte
|
||||
for (int i = 0; i < stringLength; i++)
|
||||
{
|
||||
if (reader.Buffer[reader.Offset + lengthOffset + i] != stringBytes[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If anything goes wrong, assume the string is not present
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Restore original position
|
||||
reader._position = originalPosition;
|
||||
reader.readHead = originalReadHead;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes an array of boolean values to a MessageWriter in a packed format to save space.
|
||||
/// Each byte stores up to 8 boolean values as bits.
|
||||
@@ -46,7 +138,7 @@ internal static class InnerNetClientHelper
|
||||
/// <param name="boolsEnumerable">The boolean values to write.</param>
|
||||
internal static void WriteBooleans(this MessageWriter writer, IEnumerable<bool> boolsEnumerable)
|
||||
{
|
||||
bool[] bools = boolsEnumerable.ToArray();
|
||||
bool[] bools = [.. boolsEnumerable];
|
||||
|
||||
writer.Write(bools.Length);
|
||||
|
||||
|
||||
@@ -271,17 +271,18 @@ internal static class NetworkManager
|
||||
break;
|
||||
}
|
||||
|
||||
if (innerNetObject is PlayerControl player && player != null)
|
||||
{
|
||||
if (rpcCall == (byte)RpcCalls.SetNamePlateStr)
|
||||
{
|
||||
RPC.HandleCustomRPC(player, reader);
|
||||
}
|
||||
}
|
||||
|
||||
if (Enum.IsDefined(typeof(RpcCalls), rpcCall))
|
||||
{
|
||||
innerNetObject?.HandleRpc(rpcCall, reader);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (innerNetObject is PlayerControl player && player != null)
|
||||
{
|
||||
RPC.HandleCustomRPC(player, rpcCall, reader);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using BetterAmongUs.Helpers;
|
||||
using BetterAmongUs.Managers;
|
||||
using BetterAmongUs.Modules.Support;
|
||||
using BetterAmongUs.Mono;
|
||||
using BetterAmongUs.Network;
|
||||
using BetterAmongUs.Patches.Gameplay.UI.Settings;
|
||||
using HarmonyLib;
|
||||
using Hazel;
|
||||
@@ -188,6 +189,15 @@ internal static class BetterAntiCheat
|
||||
return false;
|
||||
}
|
||||
|
||||
if (callId == (byte)RpcCalls.SetNamePlateStr)
|
||||
{
|
||||
if (RPC.IsPackedCustomRpc(reader))
|
||||
{
|
||||
reader.Recycle();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!player.IsHost())
|
||||
{
|
||||
if (callId is (byte)RpcCalls.SetTasks
|
||||
@@ -207,11 +217,11 @@ internal static class BetterAntiCheat
|
||||
if (GameState.IsInGamePlay)
|
||||
{
|
||||
if (callId is (byte)RpcCalls.SetColor
|
||||
or (byte)RpcCalls.SetHat_Deprecated
|
||||
or (byte)RpcCalls.SetSkin_Deprecated
|
||||
or (byte)RpcCalls.SetVisor_Deprecated
|
||||
or (byte)RpcCalls.SetPet_Deprecated
|
||||
or (byte)RpcCalls.SetNamePlate_Deprecated)
|
||||
or (byte)RpcCalls.SetHatStr
|
||||
or (byte)RpcCalls.SetSkinStr
|
||||
or (byte)RpcCalls.SetVisorStr
|
||||
or (byte)RpcCalls.SetPetStr
|
||||
or (byte)RpcCalls.SetNamePlateStr)
|
||||
{
|
||||
if (BetterNotificationManager.NotifyCheat(player, string.Format(Translator.GetString("AntiCheat.InvalidSetRPC"), Enum.GetName((RpcCalls)callId))))
|
||||
{
|
||||
|
||||
@@ -40,7 +40,6 @@ internal sealed class CheckAppearHandler : RPCHandler
|
||||
{
|
||||
if (!GameState.IsHost)
|
||||
{
|
||||
LogRpcInfo($"Non-host attempted CheckAppear RPC.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ internal sealed class CheckColorHandler : RPCHandler
|
||||
{
|
||||
if (!GameState.IsHost)
|
||||
{
|
||||
LogRpcInfo($"Non-host attempted CheckColor RPC");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,6 @@ internal sealed class CheckMurderHandler : RPCHandler
|
||||
{
|
||||
if (!GameState.IsHost)
|
||||
{
|
||||
LogRpcInfo($"Non-host attempted CheckMurder RPC");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ internal sealed class CheckNameHandler : RPCHandler
|
||||
{
|
||||
if (!GameState.IsHost)
|
||||
{
|
||||
LogRpcInfo($"Non-host attempted CheckName RPC");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,6 @@ internal sealed class CheckProtectHandler : RPCHandler
|
||||
{
|
||||
if (!GameState.IsHost)
|
||||
{
|
||||
LogRpcInfo($"Non-host attempted CheckProtect RPC");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,6 @@ internal sealed class CheckShapeshiftHandler : RPCHandler
|
||||
{
|
||||
if (!GameState.IsHost)
|
||||
{
|
||||
LogRpcInfo($"Non-host attempted CheckShapeshift RPC");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ internal sealed class CheckSporeHandler : RPCHandler
|
||||
{
|
||||
if (!GameState.IsHost)
|
||||
{
|
||||
LogRpcInfo($"Non-host attempted CheckSpore RPC");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@ internal sealed class CheckVanishHandler : RPCHandler
|
||||
{
|
||||
if (!GameState.IsHost)
|
||||
{
|
||||
LogRpcInfo($"Non-host attempted CheckVanish RPC");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ internal sealed class CheckZiplineHandler : RPCHandler
|
||||
{
|
||||
if (!GameState.IsHost)
|
||||
{
|
||||
LogRpcInfo($"Non-host attempted CheckZipline RPC");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using BetterAmongUs.Data;
|
||||
using BetterAmongUs.Enums;
|
||||
using BetterAmongUs.Helpers;
|
||||
using BetterAmongUs.Mono;
|
||||
using BetterAmongUs.Network;
|
||||
using Hazel;
|
||||
using Il2CppInterop.Runtime.Attributes;
|
||||
using System.Collections;
|
||||
@@ -71,10 +72,11 @@ internal sealed class HandshakeHandler
|
||||
if (HasSendSharedSecret) return;
|
||||
|
||||
HasSendSharedSecret = true;
|
||||
var writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SendSecretToPlayer, SendOption.Reliable, extendedData._Data.ClientId);
|
||||
writer.WriteBytes(SharedSecret.GetPublicKey());
|
||||
writer.Write(SharedSecret.GetTempKey());
|
||||
AmongUsClient.Instance.FinishRpcImmediately(writer);
|
||||
RPC.SendCustomRpc(CustomRPC.SendSecretToPlayer, writer =>
|
||||
{
|
||||
writer.WriteBytes(SharedSecret.GetPublicKey());
|
||||
writer.Write(SharedSecret.GetTempKey());
|
||||
}, extendedData._Data.ClientId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -116,10 +118,11 @@ internal sealed class HandshakeHandler
|
||||
int hash = SharedSecret.GetSharedSecretHash();
|
||||
// Logger.Log($"Sending secret hash: {hash} (tempKey: {tempKey})");
|
||||
|
||||
var writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.CheckSecretHashFromPlayer, SendOption.Reliable, senderClientId);
|
||||
writer.Write(tempKey);
|
||||
writer.Write(hash);
|
||||
AmongUsClient.Instance.FinishRpcImmediately(writer);
|
||||
RPC.SendCustomRpc(CustomRPC.CheckSecretHashFromPlayer, writer =>
|
||||
{
|
||||
writer.Write(tempKey);
|
||||
writer.Write(hash);
|
||||
}, senderClientId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
+69
-57
@@ -1,6 +1,6 @@
|
||||
using BetterAmongUs.Enums;
|
||||
using AmongUs.Data;
|
||||
using BetterAmongUs.Enums;
|
||||
using BetterAmongUs.Helpers;
|
||||
using BetterAmongUs.Modules;
|
||||
using BetterAmongUs.Mono;
|
||||
using Hazel;
|
||||
|
||||
@@ -8,86 +8,98 @@ namespace BetterAmongUs.Network;
|
||||
|
||||
/// <summary>
|
||||
/// Handles custom RPC (Remote Procedure Call) messages for BetterAmongUs.
|
||||
/// This class provides methods for sending and receiving custom RPC messages
|
||||
/// packed within vanilla RPC calls to maintain compatibility with vanilla servers.
|
||||
/// </summary>
|
||||
internal static class RPC
|
||||
{
|
||||
/// <summary>
|
||||
/// Processes custom RPC messages received from other players.
|
||||
/// The flag string used to identify custom RPC messages packed within vanilla RPC calls.
|
||||
/// </summary>
|
||||
/// <param name="player">The player who sent the RPC.</param>
|
||||
/// <param name="callId">The ID of the RPC call.</param>
|
||||
/// <param name="oldReader">The message reader containing the RPC data.</param>
|
||||
/// <remarks>
|
||||
/// Handles both defined custom RPCs and protects against unknown RPCs in modded lobbies.
|
||||
/// </remarks>
|
||||
internal static void HandleCustomRPC(PlayerControl player, byte callId, MessageReader oldReader)
|
||||
internal const string CUSTOM_RPC_FLAG = "bau:rpc";
|
||||
|
||||
/// <summary>
|
||||
/// Sends a custom RPC message packed within a vanilla SetNamePlateStr RPC call.
|
||||
/// This method is used to maintain compatibility with vanilla Among Us servers
|
||||
/// while allowing custom RPC communication.
|
||||
/// </summary>
|
||||
/// <param name="customRPC">The custom RPC type to send.</param>
|
||||
/// <param name="action">A delegate that writes the custom RPC payload to the message writer.</param>
|
||||
/// <param name="targetClientId">The specific client ID to target, or -1 to broadcast to all clients.</param>
|
||||
internal static void SendCustomRpc(CustomRPC customRPC, Action<MessageWriter> action, int targetClientId = -1)
|
||||
{
|
||||
if (player == null || player.IsLocalPlayer() || player.Data == null || Enum.IsDefined(typeof(RpcCalls), callId)) return;
|
||||
var writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)RpcCalls.SetNamePlateStr, SendOption.Reliable, targetClientId);
|
||||
|
||||
if (Enum.IsDefined(typeof(CustomRPC), (int)unchecked(callId)))
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles incoming custom RPC messages by extracting and processing them
|
||||
/// from the packed SetNamePlateStr RPC call.
|
||||
/// </summary>
|
||||
/// <param name="player">The player who sent the RPC message.</param>
|
||||
/// <param name="oldReader">The message reader containing the RPC data.</param>
|
||||
internal static void HandleCustomRPC(PlayerControl player, MessageReader oldReader)
|
||||
{
|
||||
if (player == null || player.IsLocalPlayer() || player.Data == null) return;
|
||||
|
||||
MessageReader reader = MessageReader.Get(oldReader);
|
||||
|
||||
_ = reader.ReadString();
|
||||
_ = reader.ReadByte();
|
||||
|
||||
if (IsPackedCustomRpc(reader))
|
||||
{
|
||||
MessageReader reader = MessageReader.Get(oldReader);
|
||||
|
||||
switch (callId)
|
||||
CustomRPC customRPC = (CustomRPC)reader.ReadByte();
|
||||
switch (customRPC)
|
||||
{
|
||||
/*
|
||||
case (byte)CustomRPC.LegacyBetterCheck:
|
||||
{
|
||||
var SetBetterUser = reader.ReadBoolean();
|
||||
var Signature = reader.ReadString();
|
||||
var Version = reader.ReadString();
|
||||
var IsVerified = Signature == Main.ModSignature.ToString();
|
||||
|
||||
if (string.IsNullOrEmpty(Signature) || string.IsNullOrEmpty(Version))
|
||||
{
|
||||
BetterNotificationManager.NotifyCheat(player, $"Invalid Action RPC: {Enum.GetName((CustomRPC)callId)} called with invalid info");
|
||||
break;
|
||||
}
|
||||
|
||||
player.BetterData().IsBetterUser = SetBetterUser;
|
||||
|
||||
if (IsVerified)
|
||||
{
|
||||
player.BetterData().IsVerifiedBetterUser = true;
|
||||
}
|
||||
|
||||
Logger.Log($"Received better user RPC from: {player.Data.PlayerName}:{player.Data.FriendCode}:{Utils.GetHashPuid(player)} - " +
|
||||
$"BetterUser: {SetBetterUser} - " +
|
||||
$"Version: {Version} - " +
|
||||
$"Verified: {IsVerified} - " +
|
||||
$"Signature: {Signature}");
|
||||
|
||||
Utils.DirtyAllNames();
|
||||
}
|
||||
break;
|
||||
*/
|
||||
case (byte)CustomRPC.SendSecretToPlayer:
|
||||
case CustomRPC.SendSecretToPlayer:
|
||||
{
|
||||
player.BetterData().HandshakeHandler.HandleSecretFromSender(reader);
|
||||
}
|
||||
break;
|
||||
case (byte)CustomRPC.CheckSecretHashFromPlayer:
|
||||
case CustomRPC.CheckSecretHashFromPlayer:
|
||||
{
|
||||
player.BetterData().HandshakeHandler.HandleSecretHashFromPlayer(reader);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (!Enum.IsDefined(typeof(CustomRPC), (int)unchecked(callId)))
|
||||
|
||||
reader.Recycle();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a MessageReader contains a packed custom RPC message.
|
||||
/// </summary>
|
||||
/// <param name="reader">The MessageReader to check for custom RPC content.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the reader contains a custom RPC flag and custom RPC data;
|
||||
/// otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
internal static bool IsPackedCustomRpc(MessageReader reader)
|
||||
{
|
||||
if (reader.BytesRemaining > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!GameState.IsHost)
|
||||
if (reader.ReadString() == CUSTOM_RPC_FLAG)
|
||||
{
|
||||
if (player.IsHost())
|
||||
{
|
||||
var Icon = Translator.GetString("BAUMark");
|
||||
var BAU = $"<color=#278720>{Icon}</color><color=#0ed400><b>{Translator.GetString("BAU")}</b></color><color=#278720>{Icon}</color>";
|
||||
Utils.DisconnectSelf(string.Format(Translator.GetString("ModdedLobbyMsg"), BAU));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user