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<Player, TrackedData> PlayerData = new Dictionary<Player, TrackedData>();
public static List<AbuseDetector> AllDetectors { get; } = new List<AbuseDetector>();
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<Player>? OnSuspicionRaised;
public event EventHandler<Player>? OnConfirmRaised;
public static void RunActorCleanup()
{
foreach (AbuseDetector allDetector in AllDetectors)
{
Dictionary<Player, TrackedData> playerData = allDetector.PlayerData;
KeyValuePair<Player, TrackedData>[] array = playerData.ToArray();
for (int i = 0; i < array.Length; i++)
{
KeyValuePair<Player, TrackedData> 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<Player, TrackedData> playerData = PlayerData;
TrackedData value2 = value;
value2.Suspicion = suspicionLevel;
playerData[player] = value2;
return suspicionLevel == SuspicionLevel.Normal;
}
}
public static class NetItemObjectManager
{
public static readonly List<Net_ItemObject> AllItemObjects = new List<Net_ItemObject>();
public static int Count => AllItemObjects.Count;
}
public class NetItemObjectTracker : NetworkBehaviour
{
private Net_ItemObject _itemObject;
public void Awake()
{
_itemObject = ((Component)this).GetComponent<Net_ItemObject>();
}
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 <EngagePanicCleanupCoroutine>d__71 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
private List<Net_ItemObject> <allItems>5__1;
private int <i>5__2;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <EngagePanicCleanupCoroutine>d__71(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<allItems>5__1 = null;
<>1__state = -2;
}
private bool MoveNext()
{
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
_isPanicModeActive = true;
<allItems>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 {<allItems>5__1.Count} items.");
<i>5__2 = 0;
goto IL_00f9;
case 2:
{
<>1__state = -1;
goto IL_00e8;
}
IL_00f9:
if (<i>5__2 < <allItems>5__1.Count)
{
if ((Object)(object)<allItems>5__1[<i>5__2] != (Object)null)
{
Object.Destroy((Object)(object)((Component)<allItems>5__1[<i>5__2]).gameObject);
}
if (<i>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:
<i>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 <PeriodicChecksCoroutine>d__62 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public PerfectGuard <>4__this;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <PeriodicChecksCoroutine>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 <ResetMessageCountersCoroutine>d__68 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <ResetMessageCountersCoroutine>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<KeyCode> _windowKey;
private Rect _windowRect;
private bool _windowShown;
private TimeSpan _abuseDetectorCleanupAccumulator;
private const float AudioSpamCooldownSeconds = 0.1f;
private static readonly Dictionary<AudioSource, float> AudioCooldowns = new Dictionary<AudioSource, float>();
private static readonly List<AudioSource> DeadAudioSources = new List<AudioSource>();
private static bool _isShieldActive;
private static Coroutine _shieldResetCoroutine;
private const int RpcSpamThreshold = 40;
private const float ShieldResetInterval = 0.25f;
private static readonly Dictionary<ushort, int> RpcMessageCounts = new Dictionary<ushort, int>();
private static readonly Dictionary<ushort, NetworkMessageDelegate> OriginalHandlers = new Dictionary<ushort, NetworkMessageDelegate>();
private static readonly HashSet<ushort> RpcWhitelist = new HashSet<ushort> { 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<bool> EnableMasterSwitch { get; private set; }
public static ConfigEntry<bool> EnableGlobalRpcShield { get; private set; }
public static ConfigEntry<bool> EnableObjectSpikeProtection { get; private set; }
public static ConfigEntry<bool> EnableAudioSpamProtection { get; private set; }
public static ConfigEntry<bool> EnableServerHealthCheck { get; private set; }
public static ConfigEntry<bool> EnableDetailedLogging { get; private set; }
public static ConfigEntry<int> 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<KeyCode>("1. General", "WindowKey", (KeyCode)290, "Key to open the configuration window.");
EnableMasterSwitch = ((BaseUnityPlugin)this).Config.Bind<bool>("1. General", "MasterSwitch", true, "Enable or disable all protections globally.");
EnableDetailedLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("1. General", "DetailedLogging", true, "Log detailed warnings for detected malicious activities.");
EnableGlobalRpcShield = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Protections", "GlobalRpcShield", true, "Enables a low-level network shield to block all types of excessive RPC spam.");
EnableObjectSpikeProtection = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Protections", "ObjectSpikeProtection", true, "Automatically detects and cleans up mass item drop spam.");
EnableAudioSpamProtection = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Protections", "AudioSpamProtection", true, "Prevents crashes from excessive audio source plays.");
EnableServerHealthCheck = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Protections", "ServerHealthCheck", true, "Monitors server latency to detect high lag or server freezes.");
NetworkObjectSpikeThreshold = ((BaseUnityPlugin)this).Config.Bind<int>("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<GUILayoutOption>());
}
}
private void DrawWindow(int windowID)
{
EnableMasterSwitch.Value = GUILayout.Toggle(EnableMasterSwitch.Value, "Enable All Protections (Master Switch)", Array.Empty<GUILayoutOption>());
GUILayout.Space(10f);
EnableGlobalRpcShield.Value = GUILayout.Toggle(EnableGlobalRpcShield.Value, "Global RPC Spam Shield", Array.Empty<GUILayoutOption>());
EnableObjectSpikeProtection.Value = GUILayout.Toggle(EnableObjectSpikeProtection.Value, "Item Drop Spike Protection", Array.Empty<GUILayoutOption>());
EnableAudioSpamProtection.Value = GUILayout.Toggle(EnableAudioSpamProtection.Value, "Audio Spam Protection", Array.Empty<GUILayoutOption>());
EnableServerHealthCheck.Value = GUILayout.Toggle(EnableServerHealthCheck.Value, "Server Health/Lag Check", Array.Empty<GUILayoutOption>());
GUILayout.Space(10f);
EnableDetailedLogging.Value = GUILayout.Toggle(EnableDetailedLogging.Value, "Enable Detailed Logging", Array.Empty<GUILayoutOption>());
GUILayout.FlexibleSpace();
GUILayout.Label("Server Status: " + DeadServerStatus, Array.Empty<GUILayoutOption>());
GUI.DragWindow();
}
[IteratorStateMachine(typeof(<PeriodicChecksCoroutine>d__62))]
private IEnumerator PeriodicChecksCoroutine()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <PeriodicChecksCoroutine>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<ushort, NetworkMessageDelegate> dictionary = field.GetValue(null) as Dictionary<ushort, NetworkMessageDelegate>;
ushort id = NetworkMessageId<RpcMessage>.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<ushort, NetworkMessageDelegate> dictionary = typeof(NetworkClient).GetField("handlers", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as Dictionary<ushort, NetworkMessageDelegate>;
ushort id = NetworkMessageId<RpcMessage>.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<RpcMessage>.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<Player>();
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(<ResetMessageCountersCoroutine>d__68))]
private static IEnumerator ResetMessageCountersCoroutine()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <ResetMessageCountersCoroutine>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<Net_ItemObject> 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<Renderer>(ref val))
{
val.enabled = false;
}
if (((Component)item).TryGetComponent<Collider>(ref val2))
{
val2.enabled = false;
}
}
}
}
[IteratorStateMachine(typeof(<EngagePanicCleanupCoroutine>d__71))]
private static IEnumerator EngagePanicCleanupCoroutine()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <EngagePanicCleanupCoroutine>d__71(0);
}
private static void PerformLatencyCheck()
{
Player mainPlayer = Player._mainPlayer;
if ((Object)(object)mainPlayer == (Object)null)
{
return;
}
if (mainPlayer.Network_isHostPlayer)
{
DeadServerStatus = "<color=cyan>Host</color>";
return;
}
float num = mainPlayer.Network_latency;
if (Mathf.Approximately(num, _previousLatency))
{
_stuckDuration += 2f;
if (_stuckDuration >= 25f && !_serverStuckReportSent)
{
_serverStuckReportSent = true;
DeadServerStatus = "<color=red>Frozen</color>";
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) ? $"<color=orange>Lagging ({num}ms)</color>" : $"<color=#2bff00>Stable ({num}ms)</color>");
}
_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<NetItemObjectTracker>() == (Object)null)
{
((Component)__instance).gameObject.AddComponent<NetItemObjectTracker>();
}
}
}
[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)
{
}
}
}