using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Mirror; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Marioalexsan.PerfectGuard")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0+5d35e5ecf46843db90d9219fbaebfe5ad12611f0")] [assembly: AssemblyProduct("PerfectGuard")] [assembly: AssemblyTitle("Marioalexsan.PerfectGuard")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Marioalexsan.PerfectGuard { public enum SuspicionLevel { Normal, Suspicious, Confirmed } internal class AbuseDetector { private struct TrackedData { public DateTime TimeSinceLastEvent; public TimeSpan TimeBetweenEventsEMA; public SuspicionLevel Suspicion; } private readonly Dictionary PlayerData = new Dictionary(); public static List AllDetectors { get; } = new List(); public double SupicionRate { get; } public double ConfirmRate { get; } public double Factor { get; } = 0.3; public TimeSpan SuspicionTimeBetweenEvents { get; } public TimeSpan ConfirmTimeBetweenEvents { get; } public event EventHandler? OnSuspicionRaised; public event EventHandler? OnConfirmRaised; public static void RunActorCleanup() { foreach (AbuseDetector allDetector in AllDetectors) { Dictionary playerData = allDetector.PlayerData; KeyValuePair[] array = playerData.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair keyValuePair = array[i]; if (!Object.op_Implicit((Object)(object)keyValuePair.Key)) { playerData.Remove(keyValuePair.Key); } } } } public AbuseDetector(double suspicionRate) : this(suspicionRate, suspicionRate * 4.0) { } public AbuseDetector(double suspicionRate, double confirmRate) { SupicionRate = suspicionRate; ConfirmRate = Math.Max(confirmRate, suspicionRate); SuspicionTimeBetweenEvents = TimeSpan.FromSeconds(1.0 / SupicionRate); ConfirmTimeBetweenEvents = TimeSpan.FromSeconds(1.0 / ConfirmRate); AllDetectors.Add(this); } public bool TrackIfServerAndCheckBehaviour(Player player) { return NetworkServer.active ? TrackEventAndCheckBehaviour(player) : CheckBehaviour(player); } public bool CheckBehaviour(Player player) { TrackedData value; return !PlayerData.TryGetValue(player, out value) || value.Suspicion == SuspicionLevel.Normal; } public bool TrackEventAndCheckBehaviour(Player player) { DateTime now = DateTime.Now; if (!PlayerData.TryGetValue(player, out var value)) { PlayerData[player] = new TrackedData { TimeSinceLastEvent = now, TimeBetweenEventsEMA = SuspicionTimeBetweenEvents * 2.0, Suspicion = SuspicionLevel.Normal }; return true; } TimeSpan timeSpan = now - value.TimeSinceLastEvent; TimeSpan timeSpan2 = Factor * timeSpan + (1.0 - Factor) * value.TimeBetweenEventsEMA; value.TimeSinceLastEvent = now; value.TimeBetweenEventsEMA = timeSpan2; SuspicionLevel suspicionLevel = SuspicionLevel.Normal; if (timeSpan2 <= ConfirmTimeBetweenEvents) { suspicionLevel = SuspicionLevel.Confirmed; if (suspicionLevel > value.Suspicion) { this.OnConfirmRaised?.Invoke(this, player); } } else if (timeSpan2 <= SuspicionTimeBetweenEvents) { suspicionLevel = SuspicionLevel.Suspicious; if (suspicionLevel > value.Suspicion) { this.OnSuspicionRaised?.Invoke(this, player); } } Dictionary playerData = PlayerData; TrackedData value2 = value; value2.Suspicion = suspicionLevel; playerData[player] = value2; return suspicionLevel == SuspicionLevel.Normal; } } public static class NetItemObjectManager { public static readonly List AllItemObjects = new List(); public static int Count => AllItemObjects.Count; } public class NetItemObjectTracker : NetworkBehaviour { private Net_ItemObject _itemObject; public void Awake() { _itemObject = ((Component)this).GetComponent(); } public override void OnStartClient() { ((NetworkBehaviour)this).OnStartClient(); if ((Object)(object)_itemObject != (Object)null && !NetItemObjectManager.AllItemObjects.Contains(_itemObject)) { NetItemObjectManager.AllItemObjects.Add(_itemObject); } } public override void OnStopClient() { ((NetworkBehaviour)this).OnStopClient(); if ((Object)(object)_itemObject != (Object)null) { NetItemObjectManager.AllItemObjects.Remove(_itemObject); } } } [BepInPlugin("Marioalexsan.PerfectGuard", "PerfectGuard", "1.1.0")] public class PerfectGuard : BaseUnityPlugin { [CompilerGenerated] private static class <>O { public static NetworkMessageDelegate <0>__ThrottledMessageHandler; } [CompilerGenerated] private sealed class d__71 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; private List 5__1; private int 5__2; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__71(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { 5__1 = null; <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; _isPanicModeActive = true; 5__1 = NetItemObjectManager.AllItemObjects.ToList(); <>2__current = null; <>1__state = 1; return true; case 1: <>1__state = -1; Logger.LogMessage((object)$"[Panic] Beginning staggered destruction of {5__1.Count} items."); 5__2 = 0; goto IL_00f9; case 2: { <>1__state = -1; goto IL_00e8; } IL_00f9: if (5__2 < 5__1.Count) { if ((Object)(object)5__1[5__2] != (Object)null) { Object.Destroy((Object)(object)((Component)5__1[5__2]).gameObject); } if (5__2 % 50 == 0) { <>2__current = null; <>1__state = 2; return true; } goto IL_00e8; } Logger.LogMessage((object)"[Panic] Item cleanup complete."); _isPanicModeActive = false; _lastNetworkObjectCount = NetItemObjectManager.Count; return false; IL_00e8: 5__2++; goto IL_00f9; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__62 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public PerfectGuard <>4__this; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__62(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Expected O, but got Unknown //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(3f); <>1__state = 1; return true; case 1: <>1__state = -1; Logger.LogInfo((object)"Periodic (slow) checks started."); if (NetworkClient.isConnected) { _lastNetworkObjectCount = NetItemObjectManager.Count; } break; case 2: <>1__state = -1; break; case 3: <>1__state = -1; break; } if (!EnableMasterSwitch.Value) { <>2__current = (object)new WaitForSeconds(5f); <>1__state = 2; return true; } if (EnableServerHealthCheck.Value && NetworkClient.isConnected) { PerformLatencyCheck(); } <>4__this.CleanupDeadAudioSources(); <>2__current = (object)new WaitForSeconds(2f); <>1__state = 3; return true; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__68 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__68(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; break; case 1: <>1__state = -1; RpcMessageCounts.Clear(); break; } <>2__current = (object)new WaitForSeconds(0.25f); <>1__state = 1; return true; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } internal static ManualLogSource Logger = null; private readonly Harmony _harmony = new Harmony("Marioalexsan.PerfectGuard"); private ConfigEntry _windowKey; private Rect _windowRect; private bool _windowShown; private TimeSpan _abuseDetectorCleanupAccumulator; private const float AudioSpamCooldownSeconds = 0.1f; private static readonly Dictionary AudioCooldowns = new Dictionary(); private static readonly List DeadAudioSources = new List(); private static bool _isShieldActive; private static Coroutine _shieldResetCoroutine; private const int RpcSpamThreshold = 40; private const float ShieldResetInterval = 0.25f; private static readonly Dictionary RpcMessageCounts = new Dictionary(); private static readonly Dictionary OriginalHandlers = new Dictionary(); private static readonly HashSet RpcWhitelist = new HashSet { 63450, 15647, 28859 }; private Coroutine _periodicChecksCoroutine; private static int _lastNetworkObjectCount; private static bool _isPanicModeActive; private const float StuckThresholdSeconds = 25f; private const float HighLatencyThreshold = 300f; private static float _previousLatency; private static float _stuckDuration; private static bool _serverStuckReportSent; public static ConfigEntry EnableMasterSwitch { get; private set; } public static ConfigEntry EnableGlobalRpcShield { get; private set; } public static ConfigEntry EnableObjectSpikeProtection { get; private set; } public static ConfigEntry EnableAudioSpamProtection { get; private set; } public static ConfigEntry EnableServerHealthCheck { get; private set; } public static ConfigEntry EnableDetailedLogging { get; private set; } public static ConfigEntry NetworkObjectSpikeThreshold { get; private set; } public static string DeadServerStatus { get; private set; } = "N/A"; public PerfectGuard() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown _windowKey = ((BaseUnityPlugin)this).Config.Bind("1. General", "WindowKey", (KeyCode)290, "Key to open the configuration window."); EnableMasterSwitch = ((BaseUnityPlugin)this).Config.Bind("1. General", "MasterSwitch", true, "Enable or disable all protections globally."); EnableDetailedLogging = ((BaseUnityPlugin)this).Config.Bind("1. General", "DetailedLogging", true, "Log detailed warnings for detected malicious activities."); EnableGlobalRpcShield = ((BaseUnityPlugin)this).Config.Bind("2. Protections", "GlobalRpcShield", true, "Enables a low-level network shield to block all types of excessive RPC spam."); EnableObjectSpikeProtection = ((BaseUnityPlugin)this).Config.Bind("2. Protections", "ObjectSpikeProtection", true, "Automatically detects and cleans up mass item drop spam."); EnableAudioSpamProtection = ((BaseUnityPlugin)this).Config.Bind("2. Protections", "AudioSpamProtection", true, "Prevents crashes from excessive audio source plays."); EnableServerHealthCheck = ((BaseUnityPlugin)this).Config.Bind("2. Protections", "ServerHealthCheck", true, "Monitors server latency to detect high lag or server freezes."); NetworkObjectSpikeThreshold = ((BaseUnityPlugin)this).Config.Bind("3. Tuning", "ObjectSpikeThreshold", 150, "The number of new items created in a single frame required to trigger a panic cleanup."); } public void Awake() { Logger = ((BaseUnityPlugin)this).Logger; _harmony.PatchAll(); Logger.LogMessage((object)"PerfectGuard v1.1.0 has loaded!"); EnableMasterSwitch.SettingChanged += delegate { ToggleSystems(); }; ToggleSystems(); } private void ToggleSystems() { if (EnableMasterSwitch.Value) { Logger.LogMessage((object)"PerfectGuard is now ACTIVE."); if (_periodicChecksCoroutine == null) { _periodicChecksCoroutine = ((MonoBehaviour)this).StartCoroutine(PeriodicChecksCoroutine()); } if (EnableGlobalRpcShield.Value) { InitializeShield(); } } else { Logger.LogWarning((object)"PerfectGuard is now INACTIVE."); if (_periodicChecksCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_periodicChecksCoroutine); _periodicChecksCoroutine = null; } ShutdownShield(); } } public void Update() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) if (Input.GetKeyDown(_windowKey.Value)) { _windowShown = !_windowShown; } if (EnableMasterSwitch.Value && EnableObjectSpikeProtection.Value && !_isPanicModeActive && NetworkClient.isConnected) { CheckForObjectSpikes(); } _abuseDetectorCleanupAccumulator += TimeSpan.FromSeconds(Time.deltaTime); if (_abuseDetectorCleanupAccumulator >= TimeSpan.FromSeconds(60.0)) { _abuseDetectorCleanupAccumulator = TimeSpan.Zero; AbuseDetector.RunActorCleanup(); } } public void OnGUI() { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) if (_windowShown) { _windowRect = GUILayout.Window(GUIUtility.GetControlID((FocusType)2), new Rect((float)Screen.width * 0.1f, (float)Screen.height * 0.1f, (float)Screen.width * 0.3f, (float)Screen.height * 0.5f), new WindowFunction(DrawWindow), "PerfectGuard v1.1.0", Array.Empty()); } } private void DrawWindow(int windowID) { EnableMasterSwitch.Value = GUILayout.Toggle(EnableMasterSwitch.Value, "Enable All Protections (Master Switch)", Array.Empty()); GUILayout.Space(10f); EnableGlobalRpcShield.Value = GUILayout.Toggle(EnableGlobalRpcShield.Value, "Global RPC Spam Shield", Array.Empty()); EnableObjectSpikeProtection.Value = GUILayout.Toggle(EnableObjectSpikeProtection.Value, "Item Drop Spike Protection", Array.Empty()); EnableAudioSpamProtection.Value = GUILayout.Toggle(EnableAudioSpamProtection.Value, "Audio Spam Protection", Array.Empty()); EnableServerHealthCheck.Value = GUILayout.Toggle(EnableServerHealthCheck.Value, "Server Health/Lag Check", Array.Empty()); GUILayout.Space(10f); EnableDetailedLogging.Value = GUILayout.Toggle(EnableDetailedLogging.Value, "Enable Detailed Logging", Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.Label("Server Status: " + DeadServerStatus, Array.Empty()); GUI.DragWindow(); } [IteratorStateMachine(typeof(d__62))] private IEnumerator PeriodicChecksCoroutine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__62(0) { <>4__this = this }; } internal static bool CheckAudioCooldown(AudioSource instance) { if (!EnableMasterSwitch.Value || !EnableAudioSpamProtection.Value || (Object)(object)instance == (Object)null) { return true; } if (!((Component)instance).gameObject.activeInHierarchy) { return false; } lock (AudioCooldowns) { if (AudioCooldowns.TryGetValue(instance, out var value) && Time.time < value) { return false; } AudioCooldowns[instance] = Time.time + 0.1f; } return true; } private void CleanupDeadAudioSources() { lock (AudioCooldowns) { DeadAudioSources.Clear(); DeadAudioSources.AddRange(AudioCooldowns.Keys.Where((AudioSource audioSource) => (Object)(object)audioSource == (Object)null)); foreach (AudioSource deadAudioSource in DeadAudioSources) { AudioCooldowns.Remove(deadAudioSource); } } } private void InitializeShield() { //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown if (_isShieldActive || !NetworkClient.active) { return; } try { FieldInfo field = typeof(NetworkClient).GetField("handlers", BindingFlags.Static | BindingFlags.NonPublic); if (field == null) { Logger.LogError((object)"[Shield] Could not find NetworkClient.handlers field!"); return; } Dictionary dictionary = field.GetValue(null) as Dictionary; ushort id = NetworkMessageId.Id; if (dictionary != null && dictionary.TryGetValue(id, out var value)) { OriginalHandlers[id] = value; object obj = <>O.<0>__ThrottledMessageHandler; if (obj == null) { NetworkMessageDelegate val = ThrottledMessageHandler; <>O.<0>__ThrottledMessageHandler = val; obj = (object)val; } dictionary[id] = (NetworkMessageDelegate)obj; if (_shieldResetCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_shieldResetCoroutine); } _shieldResetCoroutine = ((MonoBehaviour)this).StartCoroutine(ResetMessageCountersCoroutine()); _isShieldActive = true; Logger.LogMessage((object)"[Shield] Global RPC Network Shield is ACTIVE."); } } catch (Exception arg) { Logger.LogError((object)$"[Shield] Failed to initialize Network Shield: {arg}"); } } private void ShutdownShield() { if (!_isShieldActive) { return; } try { Dictionary dictionary = typeof(NetworkClient).GetField("handlers", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as Dictionary; ushort id = NetworkMessageId.Id; if (dictionary != null && OriginalHandlers.TryGetValue(id, out NetworkMessageDelegate value)) { dictionary[id] = value; } if (_shieldResetCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_shieldResetCoroutine); } _shieldResetCoroutine = null; OriginalHandlers.Clear(); RpcMessageCounts.Clear(); _isShieldActive = false; Logger.LogMessage((object)"[Shield] Global RPC Network Shield is INACTIVE."); } catch (Exception arg) { Logger.LogError((object)$"[Shield] Failed to shutdown Network Shield: {arg}"); } } private static void ThrottledMessageHandler(NetworkConnection conn, NetworkReader reader, int channelId) { if (!OriginalHandlers.TryGetValue(NetworkMessageId.Id, out NetworkMessageDelegate value)) { return; } int position = reader.Position; if (reader.Remaining < 7) { value.Invoke(conn, reader, channelId); return; } uint num = NetworkReaderExtensions.ReadUInt(reader); reader.ReadByte(); ushort num2 = NetworkReaderExtensions.ReadUShort(reader); reader.Position = position; if (RpcWhitelist.Contains(num2)) { value.Invoke(conn, reader, channelId); return; } RpcMessageCounts.TryGetValue(num2, out var value2); value2++; RpcMessageCounts[num2] = value2; if (value2 > 40) { if (value2 == 41 && EnableDetailedLogging.Value) { string arg = $"netId:{num}"; if (NetworkClient.spawned.TryGetValue(num, out var value3) && (Object)(object)value3 != (Object)null) { Player componentInParent = ((Component)value3).GetComponentInParent(); arg = (((Object)(object)componentInParent != (Object)null) ? componentInParent._nickname : ((Object)value3).name); } Logger.LogError((object)$"[Shield] RPC SPAM DETECTED! Blocking excessive calls (Hash: {num2}) from sender: {arg}."); } } else { value.Invoke(conn, reader, channelId); } } [IteratorStateMachine(typeof(d__68))] private static IEnumerator ResetMessageCountersCoroutine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__68(0); } private void CheckForObjectSpikes() { int count = NetItemObjectManager.Count; int num = count - _lastNetworkObjectCount; if (num > NetworkObjectSpikeThreshold.Value) { Logger.LogError((object)$"[Panic] Item Spike Detected! {num} new items in a single frame. Engaging Panic Cleanup."); NeutralizeAllItemsImmediately(); ((MonoBehaviour)this).StartCoroutine(EngagePanicCleanupCoroutine()); } _lastNetworkObjectCount = count; } private static void NeutralizeAllItemsImmediately() { List list = NetItemObjectManager.AllItemObjects.ToList(); if (EnableDetailedLogging.Value) { Logger.LogMessage((object)$"[Panic] Neutralizing {list.Count} items immediately."); } Renderer val = default(Renderer); Collider val2 = default(Collider); foreach (Net_ItemObject item in list) { if (!((Object)(object)item == (Object)null) && !((Object)(object)((Component)item).gameObject == (Object)null)) { if (((Component)item).TryGetComponent(ref val)) { val.enabled = false; } if (((Component)item).TryGetComponent(ref val2)) { val2.enabled = false; } } } } [IteratorStateMachine(typeof(d__71))] private static IEnumerator EngagePanicCleanupCoroutine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__71(0); } private static void PerformLatencyCheck() { Player mainPlayer = Player._mainPlayer; if ((Object)(object)mainPlayer == (Object)null) { return; } if (mainPlayer.Network_isHostPlayer) { DeadServerStatus = "Host"; return; } float num = mainPlayer.Network_latency; if (Mathf.Approximately(num, _previousLatency)) { _stuckDuration += 2f; if (_stuckDuration >= 25f && !_serverStuckReportSent) { _serverStuckReportSent = true; DeadServerStatus = "Frozen"; Logger.LogError((object)"[HealthCheck] Server is unresponsive (latency is frozen)."); } } else { if (_serverStuckReportSent) { Logger.LogMessage((object)"[HealthCheck] Server has recovered and is responsive again."); } _stuckDuration = 0f; _serverStuckReportSent = false; } if (!_serverStuckReportSent) { DeadServerStatus = ((num > 300f) ? $"Lagging ({num}ms)" : $"Stable ({num}ms)"); } _previousLatency = num; } } internal static class ModInfo { public const string GUID = "Marioalexsan.PerfectGuard"; public const string NAME = "PerfectGuard"; public const string VERSION = "1.1.0"; } } namespace Marioalexsan.PerfectGuard.Patches { [HarmonyPatch] internal static class CommandRateLimiter { private static readonly AbuseDetector DropCommandDetector; static CommandRateLimiter() { DropCommandDetector = new AbuseDetector(5.0); DropCommandDetector.OnConfirmRaised += delegate(object sender, Player player) { if (PerfectGuard.EnableDetailedLogging.Value) { PerfectGuard.Logger.LogWarning((object)("Player " + ((Object)player).name + " is spamming the drop item command! Blocking requests.")); } }; } [HarmonyPatch(typeof(PlayerInventory), "UserCode_Cmd_DropItem__ItemData__Int32")] [HarmonyPrefix] private static bool RateLimitDropCommand(PlayerInventory __instance) { if (!NetworkServer.active) { return true; } return DropCommandDetector.TrackIfServerAndCheckBehaviour(__instance._player); } } [HarmonyPatch] public class NetItemObjectTrackerInjector { [HarmonyPatch(typeof(Net_ItemObject), "Awake")] [HarmonyPostfix] private static void InjectTracker(Net_ItemObject __instance) { if ((Object)(object)((Component)__instance).GetComponent() == (Object)null) { ((Component)__instance).gameObject.AddComponent(); } } } [HarmonyPatch] internal static class AudioSpamPatches { [HarmonyPrefix] [HarmonyPatch(typeof(AudioSource), "Play", new Type[] { })] public static bool AudioSource_Play_Prefix(AudioSource __instance) { return PerfectGuard.CheckAudioCooldown(__instance); } [HarmonyPrefix] [HarmonyPatch(typeof(AudioSource), "PlayOneShot", new Type[] { typeof(AudioClip), typeof(float) })] public static bool AudioSource_PlayOneShot_Prefix(AudioSource __instance) { return PerfectGuard.CheckAudioCooldown(__instance); } } [HarmonyPatch] internal static class EffectSpamPatches { private static readonly AbuseDetector TeleportDetector; private static readonly AbuseDetector SparkleDetector; private static readonly AbuseDetector PoofDetector; private static readonly AbuseDetector JumpDetector; static EffectSpamPatches() { TeleportDetector = new AbuseDetector(4.0); SparkleDetector = new AbuseDetector(4.0); PoofDetector = new AbuseDetector(4.0); JumpDetector = new AbuseDetector(4.0); TeleportDetector.OnConfirmRaised += delegate(object s, Player p) { LogConfirmation(p, "teleport effects"); }; SparkleDetector.OnConfirmRaised += delegate(object s, Player p) { LogConfirmation(p, "vanity sparkle effects"); }; PoofDetector.OnConfirmRaised += delegate(object s, Player p) { LogConfirmation(p, "poof smoke effects"); }; JumpDetector.OnConfirmRaised += delegate(object s, Player p) { LogConfirmation(p, "jump attack effects"); }; } private static void LogConfirmation(Player player, string effectName) { if (PerfectGuard.EnableDetailedLogging.Value) { PerfectGuard.Logger.LogWarning((object)("Player " + ((Object)player).name + " is malicious! Trying to crash the server using " + effectName + ".")); } } [HarmonyPrefix] [HarmonyPatch(typeof(PlayerVisual), "Cmd_PlayTeleportEffect")] private static bool RateLimitTeleportCommand(PlayerVisual __instance) { return TeleportDetector.TrackIfServerAndCheckBehaviour(__instance._player); } [HarmonyPrefix] [HarmonyPatch(typeof(PlayerVisual), "Rpc_PlayTeleportEffect")] private static bool CheckServerTeleport(PlayerVisual __instance) { return TeleportDetector.CheckBehaviour(__instance._player); } [HarmonyPrefix] [HarmonyPatch(typeof(PlayerVisual), "Cmd_VanitySparkleEffect")] private static bool RateLimitSparkleCommand(PlayerVisual __instance) { return SparkleDetector.TrackIfServerAndCheckBehaviour(__instance._player); } [HarmonyPrefix] [HarmonyPatch(typeof(PlayerVisual), "Rpc_VanitySparkleEffect")] private static bool CheckServerSparkle(PlayerVisual __instance) { return SparkleDetector.CheckBehaviour(__instance._player); } [HarmonyPrefix] [HarmonyPatch(typeof(PlayerVisual), "Cmd_PoofSmokeEffect")] private static bool RateLimitPoofCommand(PlayerVisual __instance) { return PoofDetector.TrackIfServerAndCheckBehaviour(__instance._player); } [HarmonyPrefix] [HarmonyPatch(typeof(PlayerVisual), "Rpc_PoofSmokeEffect")] private static bool CheckServerPoof(PlayerVisual __instance) { return PoofDetector.CheckBehaviour(__instance._player); } [HarmonyPrefix] [HarmonyPatch(typeof(PlayerVisual), "Cmd_JumpAttackEffect")] private static bool RateLimitJumpCommand(PlayerVisual __instance) { return JumpDetector.TrackIfServerAndCheckBehaviour(__instance._player); } [HarmonyPrefix] [HarmonyPatch(typeof(PlayerVisual), "Rpc_JumpAttackEffect")] private static bool CheckServerJump(PlayerVisual __instance) { return JumpDetector.CheckBehaviour(__instance._player); } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }