using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppScheduleOne.EntityFramework; using Il2CppScheduleOne.ItemFramework; using Il2CppScheduleOne.PlayerScripts; using Il2CppTMPro; using MelonLoader; using MelonLoader.Preferences; using Microsoft.CodeAnalysis; using MultiplayerPVP.Core; using PvpCameraEsp; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: MelonAdditionalDependencies(new string[] { "MultiplayerPVP" })] [assembly: MelonOptionalDependencies(new string[] { "NetEye" })] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: MelonInfo(typeof(PvpCameraEspMod), "PvpCameraEsp", "1.0.3", "Virtunerd", null)] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: AssemblyFileVersion("1.0.3")] [assembly: AssemblyInformationalVersion("1.0.3")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.3.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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 PvpCameraEsp { internal static class CameraBuildTracker { private static bool _active; private static float _graceUntil; private const float GraceSeconds = 2.5f; private static bool _hooked; internal static bool Hooked => _hooked; internal static bool LocalPlacingCamera { get { try { return _active || Time.unscaledTime < _graceUntil; } catch { return false; } } } internal static void Apply(Harmony h) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Expected O, but got Unknown try { Type type = ResolveBuildManager(); if (type == null) { MelonLogger.Warning("[PvpCameraEsp] BuildManager type not found — ownership falls back to proximity."); return; } MethodInfo methodInfo = FindMethod(type, "StartBuilding"); if (methodInfo == null) { MelonLogger.Warning("[PvpCameraEsp] BuildManager.StartBuilding not found — ownership falls back to proximity."); return; } MethodInfo method = typeof(CameraBuildTracker).GetMethod("StartPostfix", BindingFlags.Static | BindingFlags.NonPublic); h.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); MethodInfo methodInfo2 = FindMethod(type, "StopBuilding"); if (methodInfo2 != null) { MethodInfo method2 = typeof(CameraBuildTracker).GetMethod("StopPostfix", BindingFlags.Static | BindingFlags.NonPublic); h.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(method2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } _hooked = true; MelonLogger.Msg("[PvpCameraEsp] camera-placement tracker armed (BuildManager hooked)."); } catch (Exception ex) { MelonLogger.Warning("[PvpCameraEsp] CameraBuildTracker.Apply: " + ex.Message); } } private static void StartPostfix(ItemInstance __0) { try { string text = ItemId(__0); if (EspConfig.DebugLog.Value) { MelonLogger.Msg("[PvpCameraEsp] StartBuilding item='" + (text ?? "?") + "'"); } if (!string.IsNullOrEmpty(text) && text.IndexOf("NetEye", StringComparison.OrdinalIgnoreCase) >= 0 && text.IndexOf("Camera", StringComparison.OrdinalIgnoreCase) >= 0) { _active = true; _graceUntil = Time.unscaledTime + 2.5f; if (EspConfig.DebugLog.Value) { MelonLogger.Msg("[PvpCameraEsp] local CAMERA placement STARTED → next new camera = mine"); } } } catch { } } private static void StopPostfix() { try { bool active = _active; _active = false; if (active) { _graceUntil = Time.unscaledTime + 2.5f; if (EspConfig.DebugLog.Value) { MelonLogger.Msg("[PvpCameraEsp] local camera placement STOPPED (grace armed)"); } } } catch { } } private static string ItemId(ItemInstance item) { try { if (item == null) { return null; } object member = GetMember(item, "Definition"); if (member == null) { return null; } return GetMember(member, "ID") as string; } catch { return null; } } private static object GetMember(object target, string name) { if (target == null) { return null; } Type type = target.GetType(); try { PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); if (property != null) { return property.GetValue(target); } } catch { } try { FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); if (field != null) { return field.GetValue(target); } } catch { } return null; } private static MethodInfo FindMethod(Type t, string name) { try { return t.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } catch (AmbiguousMatchException) { MethodInfo methodInfo = null; MethodInfo[] methods = t.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo2 in methods) { if (!(methodInfo2.Name != name)) { if (methodInfo == null) { methodInfo = methodInfo2; } ParameterInfo[] parameters = methodInfo2.GetParameters(); if (parameters.Length == 1) { methodInfo = methodInfo2; break; } } } return methodInfo; } catch { return null; } } private static Type ResolveBuildManager() { Type type = FindType("Il2CppScheduleOne.Building.BuildManager") ?? FindType("ScheduleOne.Building.BuildManager"); if (type != null) { return type; } try { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { if (assembly.GetName().Name != "Assembly-CSharp") { continue; } Type[] types; try { types = assembly.GetTypes(); } catch { continue; } Type[] array = types; foreach (Type type2 in array) { if (type2.Name == "BuildManager" && type2.Namespace != null && type2.Namespace.IndexOf("ScheduleOne", StringComparison.Ordinal) >= 0) { return type2; } } } } catch { } return null; } private static Type FindType(string fullName) { try { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { Type type = assembly.GetType(fullName); if (type != null) { return type; } } } catch { } try { return Type.GetType(fullName + ", Assembly-CSharp"); } catch { return null; } } internal static void Reset() { _active = false; _graceUntil = 0f; } } internal static class CameraGuard { internal static void Apply() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown try { Harmony h = new Harmony("com.virtunerd.pvpcameraesp"); Type typeFromHandle = typeof(BuildableItem); PatchVoid(h, typeFromHandle, "PickupItem"); PatchCan(h, typeFromHandle, "CanBePickedUp"); PatchCan(h, typeFromHandle, "CanBeDestroyed"); CameraBuildTracker.Apply(h); } catch (Exception ex) { MelonLogger.Warning("[PvpCameraEsp] guard apply: " + ex.Message); } } private static bool ShouldBlock(BuildableItem bi) { try { if (!EspConfig.ProtectCameras.Value) { return false; } if (!PvpApi.IsMatchActive) { return false; } if ((Object)(object)bi == (Object)null) { return false; } GameObject gameObject = ((Component)bi).gameObject; if ((Object)(object)gameObject == (Object)null) { return false; } if (!IsNetEyeCameraDeployable(gameObject)) { return false; } if (CameraOwnership.IsMine(((Object)gameObject).GetInstanceID())) { return false; } return true; } catch { return false; } } private static bool IsNetEyeCameraDeployable(GameObject go) { try { Transform transform = go.transform; int childCount = transform.childCount; for (int i = 0; i < childCount; i++) { Transform child = transform.GetChild(i); if ((Object)(object)child != (Object)null && ((Object)child).name != null && ((Object)child).name.StartsWith("NetEye_")) { return true; } } } catch { } return false; } private static bool BlockVoidPrefix(BuildableItem __instance) { try { if (ShouldBlock(__instance)) { return false; } } catch { } return true; } private static bool CanPrefix(BuildableItem __instance, ref string reason, ref bool __result) { try { if (ShouldBlock(__instance)) { reason = "Protected PvP camera"; __result = false; return false; } } catch { } return true; } private static void PatchVoid(Harmony h, Type t, string method) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown try { MethodInfo method2 = t.GetMethod(method, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method2 == null) { MelonLogger.Warning("[PvpCameraEsp] guard: " + method + " not found"); return; } MethodInfo method3 = typeof(CameraGuard).GetMethod("BlockVoidPrefix", BindingFlags.Static | BindingFlags.NonPublic); h.Patch((MethodBase)method2, new HarmonyMethod(method3), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } catch (Exception ex) { MelonLogger.Warning("[PvpCameraEsp] guard patch " + method + ": " + ex.Message); } } private static void PatchCan(Harmony h, Type t, string method) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown try { MethodInfo method2 = t.GetMethod(method, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(method2 == null)) { MethodInfo method3 = typeof(CameraGuard).GetMethod("CanPrefix", BindingFlags.Static | BindingFlags.NonPublic); h.Patch((MethodBase)method2, new HarmonyMethod(method3), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } catch (Exception ex) { MelonLogger.Warning("[PvpCameraEsp] guard patch " + method + ": " + ex.Message); } } } internal static class CameraOwnership { private static readonly HashSet _known = new HashSet(); private static readonly Dictionary _pending = new Dictionary(); private static readonly Dictionary _mine = new Dictionary(); private const float ClaimWindow = 2.5f; private const float PlacingClaimRadius = 10f; private static readonly Dictionary _pendingPickup = new Dictionary(); private static float _nextPickupTry; private static int _pickupRounds; private const float PickupInterval = 1.5f; private const int MaxPickupRounds = 12; internal static bool PickupPending => _pendingPickup.Count > 0; internal static bool IsMine(int deployableInstanceId) { return _mine.ContainsKey(deployableInstanceId); } internal static bool IsMineCamera(Camera cam) { try { if ((Object)(object)cam == (Object)null) { return false; } BuildableItem val = FindDeployable(cam); if ((Object)(object)val == (Object)null) { return false; } return _mine.ContainsKey(((Object)((Component)val).gameObject).GetInstanceID()); } catch { return false; } } internal static void Tick(float radius) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) try { bool localPlacingCamera = CameraBuildTracker.LocalPlacingCamera; bool hooked = CameraBuildTracker.Hooked; float unscaledTime = Time.unscaledTime; Player localPlayer = PvpApi.LocalPlayer; bool flag = (Object)(object)localPlayer != (Object)null; Vector3 val = Vector3.zero; if (flag) { try { val = ((Component)localPlayer).transform.position; } catch { flag = false; } } IReadOnlyList readOnlyList = NetEyeBridge.AllCameras(); for (int i = 0; i < readOnlyList.Count; i++) { Camera cam = readOnlyList[i].Cam; if ((Object)(object)cam == (Object)null) { continue; } BuildableItem val2 = FindDeployable(cam); if ((Object)(object)val2 == (Object)null) { continue; } int instanceID; try { instanceID = ((Object)((Component)val2).gameObject).GetInstanceID(); } catch { continue; } if (_mine.ContainsKey(instanceID) || _known.Contains(instanceID) || _pendingPickup.ContainsKey(instanceID)) { continue; } if (localPlacingCamera) { bool flag2 = !flag; if (flag) { try { Vector3 val3 = ((Component)val2).transform.position - val; val3.y = 0f; flag2 = ((Vector3)(ref val3)).magnitude <= 10f; } catch { flag2 = true; } } if (flag2) { _mine[instanceID] = val2; _pending.Remove(instanceID); _known.Add(instanceID); if (EspConfig.DebugLog.Value) { MelonLogger.Msg("[PvpCameraEsp] claimed own camera (local build flow)"); } } continue; } if (hooked) { _known.Add(instanceID); _pending.Remove(instanceID); continue; } if (!_pending.TryGetValue(instanceID, out var value)) { _pending[instanceID] = unscaledTime; value = unscaledTime; } bool flag3 = false; if (flag) { try { Vector3 val4 = ((Component)val2).transform.position - val; val4.y = 0f; flag3 = ((Vector3)(ref val4)).magnitude <= radius; } catch { } } if (flag3) { _mine[instanceID] = val2; _pending.Remove(instanceID); _known.Add(instanceID); } else if (unscaledTime - value > 2.5f) { _pending.Remove(instanceID); _known.Add(instanceID); } } } catch { } } private static BuildableItem FindDeployable(Camera cam) { try { Transform val = ((Component)cam).transform; int num = 0; while ((Object)(object)val != (Object)null && num++ < 64) { BuildableItem component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null) { return component; } val = val.parent; } } catch { } return null; } internal static void QueuePickUpMine() { foreach (KeyValuePair item in _mine) { if ((Object)(object)item.Value != (Object)null) { _pendingPickup[item.Key] = item.Value; } } _pickupRounds = 0; _nextPickupTry = 0f; _mine.Clear(); _known.Clear(); _pending.Clear(); } internal static void TickPickupRetry() { if (_pendingPickup.Count == 0) { return; } float unscaledTime = Time.unscaledTime; if (unscaledTime < _nextPickupTry) { return; } _nextPickupTry = unscaledTime + 1.5f; bool value = EspConfig.DebugLog.Value; List list = new List(); foreach (KeyValuePair item in _pendingPickup) { BuildableItem value2 = item.Value; if ((Object)(object)value2 == (Object)null) { list.Add(item.Key); continue; } bool flag = false; try { flag = value2.IsDestroyed; } catch { } if (flag) { list.Add(item.Key); continue; } string value3 = null; bool flag2 = false; try { flag2 = value2.CanBePickedUp(ref value3); } catch { } if (value) { MelonLogger.Msg($"[PvpCameraEsp] pickup-retry: can={flag2} reason='{value3}' host={PvpApi.IsHost}"); } if (!flag2) { continue; } try { value2.PickupItem(); } catch (Exception ex) { if (value) { MelonLogger.Msg("[PvpCameraEsp] pickup-retry threw: " + ex.Message); } } } for (int i = 0; i < list.Count; i++) { _pendingPickup.Remove(list[i]); } if (_pendingPickup.Count > 0 && ++_pickupRounds >= 12) { if (value) { MelonLogger.Warning($"[PvpCameraEsp] gave up restoring {_pendingPickup.Count} camera(s) after {12} tries"); } _pendingPickup.Clear(); } } internal static void AbortPickup() { _pendingPickup.Clear(); _pickupRounds = 0; } internal static void Reset() { _known.Clear(); _mine.Clear(); _pending.Clear(); _pendingPickup.Clear(); _pickupRounds = 0; } } internal struct VisibleEnemy { internal Player Player; internal int Team; internal ulong SteamId; internal string Persona; internal Vector3 ViewportFeet; internal Vector3 ViewportTop; } internal static class EnemyTracker { private static readonly List _result = new List(8); internal static List Scan(Camera cam) { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_0264: Unknown result type (might be due to invalid IL or missing references) _result.Clear(); if ((Object)(object)cam == (Object)null) { return _result; } int localTeam = PvpApi.LocalTeam; if (localTeam < 0) { return _result; } Player localPlayer = PvpApi.LocalPlayer; List players = PvpApi.Players; float value = EspConfig.MaxDistance.Value; float num = Mathf.Max(0.5f, EspConfig.BodyHeight.Value); bool value2 = EspConfig.RequireLineOfSight.Value; Vector3 position = ((Component)cam).transform.position; ulong steamId = default(ulong); string persona = default(string); for (int i = 0; i < players.Count; i++) { Player val = players[i]; if ((Object)(object)val == (Object)null || ((Object)(object)localPlayer != (Object)null && (val == localPlayer || (Object)(object)val == (Object)(object)localPlayer))) { continue; } int team = PvpApi.GetTeam(val); if (team < 0 || team == localTeam) { continue; } Vector3 position2; Vector3 val2; Vector3 val3; try { position2 = ((Component)val).transform.position; val2 = position2 + Vector3.up * num; val3 = position2 + Vector3.up * (num * 0.5f); } catch { continue; } if (Vector3.Distance(position, val3) > value) { continue; } Vector3 val4; Vector3 val5; try { val4 = cam.WorldToViewportPoint(position2); val5 = cam.WorldToViewportPoint(val2); } catch { continue; } if (!(val4.z <= 0f) && !(val5.z <= 0f)) { float num2 = (val4.x + val5.x) * 0.5f; float num3 = (val4.y + val5.y) * 0.5f; if (!(num2 < -0.2f) && !(num2 > 1.2f) && !(num3 < -0.2f) && !(num3 > 1.2f) && (!value2 || !IsOccluded(position, val3, val))) { PvpApi.TryGetIdentity(val, ref steamId, ref persona); _result.Add(new VisibleEnemy { Player = val, Team = team, SteamId = steamId, Persona = persona, ViewportFeet = val4, ViewportTop = val5 }); } } } return _result; } private static bool IsOccluded(Vector3 from, Vector3 to, Player target) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) try { Vector3 val = to - from; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude < 0.05f) { return false; } RaycastHit val2 = default(RaycastHit); if (!Physics.Raycast(from, val / magnitude, ref val2, magnitude - 0.3f)) { return false; } if ((Object)(object)((RaycastHit)(ref val2)).collider == (Object)null) { return false; } Player componentInParent = ((Component)((RaycastHit)(ref val2)).collider).GetComponentInParent(); return !((Object)(object)componentInParent != (Object)null) || (componentInParent != target && !((Object)(object)componentInParent == (Object)(object)target)); } catch { return false; } } } internal static class EspConfig { private static MelonPreferences_Category _cat; internal static MelonPreferences_Entry Enabled; internal static MelonPreferences_Entry MaxDistance; internal static MelonPreferences_Entry RequireLineOfSight; internal static MelonPreferences_Entry ReportCooldownSeconds; internal static MelonPreferences_Entry RemindWhileVisibleSeconds; internal static MelonPreferences_Entry ShowBox; internal static MelonPreferences_Entry BoxThickness; internal static MelonPreferences_Entry BodyHeight; internal static MelonPreferences_Entry BoxColorMode; internal static MelonPreferences_Entry BoxColor; internal static MelonPreferences_Entry ShowLabels; internal static MelonPreferences_Entry LabelFontSize; internal static MelonPreferences_Entry DebugLog; internal static MelonPreferences_Entry ProtectCameras; internal static MelonPreferences_Entry OwnPlacementRadius; internal static MelonPreferences_Entry CleanupOwnCamerasOnRoundEnd; internal static MelonPreferences_Entry CleanStaleCameras; internal static void Load() { _cat = MelonPreferences.CreateCategory("PvpCameraEsp"); Enabled = _cat.CreateEntry("Enabled", true, (string)null, "Master toggle for the camera ESP.", false, false, (ValueValidator)null, (string)null); MaxDistance = _cat.CreateEntry("MaxDistance", 80f, (string)null, "Max distance (m) from the camera to detect an enemy (camera far clip is ~80).", false, false, (ValueValidator)null, (string)null); RequireLineOfSight = _cat.CreateEntry("RequireLineOfSight", true, (string)null, "Only detect enemies with a clear line of sight (not behind walls). Disable if detection misses.", false, false, (ValueValidator)null, (string)null); ReportCooldownSeconds = _cat.CreateEntry("ReportCooldownSeconds", 5f, (string)null, "Re-arm window: an enemy must be GONE from a camera this long before a NEW alert fires (edge-triggered, no spam while they stay in view).", false, false, (ValueValidator)null, (string)null); RemindWhileVisibleSeconds = _cat.CreateEntry("RemindWhileVisibleSeconds", 0f, (string)null, "Optional: re-alert every N seconds while an enemy stays continuously in view (0 = off, alert only on enter).", false, false, (ValueValidator)null, (string)null); ShowBox = _cat.CreateEntry("ShowBox", true, (string)null, "Draw a box around each spotted enemy on the camera feed (ESP box).", false, false, (ValueValidator)null, (string)null); BoxThickness = _cat.CreateEntry("BoxThickness", 2, (string)null, "Border thickness (px) of the ESP box.", false, false, (ValueValidator)null, (string)null); BodyHeight = _cat.CreateEntry("BodyHeight", 2f, (string)null, "Assumed player height (m) used to size the box from feet to head.", false, false, (ValueValidator)null, (string)null); BoxColorMode = _cat.CreateEntry("BoxColorMode", "enemyteam", (string)null, "Box/label colour source: 'enemyteam' (spotted enemy's team colour), 'ownteam' (your team = the camera owner's team), or 'custom' (use BoxColor).", false, false, (ValueValidator)null, (string)null); BoxColor = _cat.CreateEntry("BoxColor", "FF3030", (string)null, "Custom box/label colour as hex RGB or RGBA (e.g. FF3030 or FF3030C0). Used only when BoxColorMode = custom.", false, false, (ValueValidator)null, (string)null); ShowLabels = _cat.CreateEntry("ShowLabels", true, (string)null, "Draw the enemy's Steam name above the box on the camera feed.", false, false, (ValueValidator)null, (string)null); LabelFontSize = _cat.CreateEntry("LabelFontSize", 20, (string)null, "Font size of the on-feed name labels.", false, false, (ValueValidator)null, (string)null); DebugLog = _cat.CreateEntry("DebugLog", false, (string)null, "Verbose logging (cameras found, alerts) for diagnosing the live test.", false, false, (ValueValidator)null, (string)null); ProtectCameras = _cat.CreateEntry("ProtectCameras", true, (string)null, "During a Teams match, block players from picking up / demolishing cameras they didn't place (protects client cameras from the host & host-team, and vice-versa).", false, false, (ValueValidator)null, (string)null); OwnPlacementRadius = _cat.CreateEntry("OwnPlacementRadius", 4f, (string)null, "FALLBACK ONLY (used if the BuildManager hook isn't available): a newly-placed camera within this horizontal distance (m) of you is treated as YOURS. Normally ownership comes from the local build flow and this is ignored.", false, false, (ValueValidator)null, (string)null); CleanupOwnCamerasOnRoundEnd = _cat.CreateEntry("CleanupOwnCamerasOnRoundEnd", true, (string)null, "Pick up your own cameras (back into your inventory) automatically when a match ends, so they don't linger in the NetEye app. Applies to both host and client.", false, false, (ValueValidator)null, (string)null); CleanStaleCameras = _cat.CreateEntry("CleanStaleCameras", true, (string)null, "HOST: remove stale camera entries (loaded from a previous session's cameras.txt, shown in the app but not backed by a live camera) while in a multiplayer match.", false, false, (ValueValidator)null, (string)null); } internal static Color ResolveBoxColor(int enemyTeam) { //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) string text = (BoxColorMode.Value ?? "").Trim().ToLowerInvariant(); if (text == "ownteam") { int localTeam = PvpApi.LocalTeam; return PvpApi.TeamColor((localTeam >= 0) ? localTeam : 0); } if (text == "custom") { string value = BoxColor.Value; Color result = default(Color); if (!string.IsNullOrEmpty(value) && ColorUtility.TryParseHtmlString((value[0] == '#') ? value : ("#" + value), ref result)) { return result; } return Color.red; } return PvpApi.TeamColor(enemyTeam); } } internal static class FeedOverlay { private sealed class EnemyBox { internal RectTransform Root; internal RawImage Top; internal RawImage Bottom; internal RawImage Left; internal RawImage Right; internal TextMeshProUGUI Label; } private static RawImage _bound; private static RectTransform _container; private static TMP_FontAsset _font; private static bool _fontResolved; private static bool _warnedNoFont; private static Texture2D _white; private static readonly Dictionary _boxes = new Dictionary(); private static readonly HashSet _seen = new HashSet(); private static Texture2D White { get { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown //IL_0024: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_white == (Object)null) { _white = new Texture2D(1, 1); _white.SetPixel(0, 0, Color.white); _white.Apply(); } return _white; } } internal static void Update(RawImage feed, List enemies) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_02a5: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)feed == (Object)null) { HideAll(); return; } bool value = EspConfig.ShowBox.Value; bool value2 = EspConfig.ShowLabels.Value; if (!value && !value2) { HideAll(); return; } EnsureContainer(feed); if ((Object)(object)_container == (Object)null) { return; } TMP_FontAsset font = ResolveFont(); Rect rect = ((Graphic)feed).rectTransform.rect; Vector2 size = ((Rect)(ref rect)).size; int value3 = EspConfig.LabelFontSize.Value; _seen.Clear(); Vector2 val = default(Vector2); Vector2 val2 = default(Vector2); Vector2 anchoredPosition = default(Vector2); for (int i = 0; i < enemies.Count; i++) { VisibleEnemy visibleEnemy = enemies[i]; string text = ((visibleEnemy.SteamId != 0L) ? visibleEnemy.SteamId.ToString() : ("n:" + visibleEnemy.Persona)); _seen.Add(text); EnemyBox orCreate = GetOrCreate(text, font); if (orCreate == null) { continue; } ((Vector2)(ref val))..ctor((visibleEnemy.ViewportFeet.x - 0.5f) * size.x, (visibleEnemy.ViewportFeet.y - 0.5f) * size.y); ((Vector2)(ref val2))..ctor((visibleEnemy.ViewportTop.x - 0.5f) * size.x, (visibleEnemy.ViewportTop.y - 0.5f) * size.y); float num = Mathf.Max(6f, Mathf.Abs(val2.y - val.y)); float num2 = Mathf.Clamp(num * 0.4f, 6f, num); ((Vector2)(ref anchoredPosition))..ctor((val.x + val2.x) * 0.5f, (val.y + val2.y) * 0.5f); orCreate.Root.anchoredPosition = anchoredPosition; orCreate.Root.sizeDelta = new Vector2(num2, num); Color val3 = EspConfig.ResolveBoxColor(visibleEnemy.Team); SetEdge(orCreate.Top, value, val3); SetEdge(orCreate.Bottom, value, val3); SetEdge(orCreate.Left, value, val3); SetEdge(orCreate.Right, value, val3); if ((Object)(object)orCreate.Label != (Object)null) { ((Component)orCreate.Label).gameObject.SetActive(value2); if (value2) { ((TMP_Text)orCreate.Label).text = visibleEnemy.Persona; ((TMP_Text)orCreate.Label).fontSize = value3; ((Graphic)orCreate.Label).color = val3; } } if (!((Component)orCreate.Root).gameObject.activeSelf) { ((Component)orCreate.Root).gameObject.SetActive(true); } } foreach (KeyValuePair box in _boxes) { if (!_seen.Contains(box.Key) && (Object)(object)box.Value.Root != (Object)null && ((Component)box.Value.Root).gameObject.activeSelf) { ((Component)box.Value.Root).gameObject.SetActive(false); } } } private static void SetEdge(RawImage edge, bool show, Color col) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)edge == (Object)null)) { if (((Component)edge).gameObject.activeSelf != show) { ((Component)edge).gameObject.SetActive(show); } if (show) { ((Graphic)edge).color = col; } } } internal static void HideAll() { try { foreach (KeyValuePair box in _boxes) { if ((Object)(object)box.Value.Root != (Object)null && ((Component)box.Value.Root).gameObject.activeSelf) { ((Component)box.Value.Root).gameObject.SetActive(false); } } } catch { } } private static void EnsureContainer(RawImage feed) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_bound == (Object)(object)feed && (Object)(object)_container != (Object)null) { return; } _boxes.Clear(); _bound = feed; try { GameObject val = new GameObject("PvpCamEsp_Overlay"); RectTransform val2 = val.AddComponent(); ((Transform)val2).SetParent(((Component)feed).transform, false); val2.anchorMin = Vector2.zero; val2.anchorMax = Vector2.one; val2.offsetMin = Vector2.zero; val2.offsetMax = Vector2.zero; val.AddComponent(); _container = val2; if (EspConfig.DebugLog.Value) { MelonLogger.Msg("[PvpCameraEsp] overlay container attached to feed"); } } catch { _container = null; } } private static EnemyBox GetOrCreate(string key, TMP_FontAsset font) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) if (_boxes.TryGetValue(key, out var value) && (Object)(object)value.Root != (Object)null) { return value; } try { int num = Mathf.Max(1, EspConfig.BoxThickness.Value); RectTransform val = NewRect("box_" + key, (Transform)(object)_container, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f)); value = new EnemyBox { Root = val }; value.Top = Edge((Transform)(object)val, new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, (float)num)); value.Bottom = Edge((Transform)(object)val, new Vector2(0f, 0f), new Vector2(1f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, (float)num)); value.Left = Edge((Transform)(object)val, new Vector2(0f, 0f), new Vector2(0f, 1f), new Vector2(0f, 0.5f), new Vector2((float)num, 0f)); value.Right = Edge((Transform)(object)val, new Vector2(1f, 0f), new Vector2(1f, 1f), new Vector2(1f, 0.5f), new Vector2((float)num, 0f)); if ((Object)(object)font != (Object)null) { RectTransform val2 = NewRect("name", (Transform)(object)val, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0.5f, 0f)); val2.sizeDelta = new Vector2(260f, 28f); val2.anchoredPosition = new Vector2(0f, 4f); TextMeshProUGUI val3 = ((Component)val2).gameObject.AddComponent(); ((TMP_Text)val3).font = font; ((TMP_Text)val3).alignment = (TextAlignmentOptions)1026; ((TMP_Text)val3).enableWordWrapping = false; ((TMP_Text)val3).richText = false; ((TMP_Text)val3).fontStyle = (FontStyles)1; ((TMP_Text)val3).overflowMode = (TextOverflowModes)0; ((Graphic)val3).raycastTarget = false; value.Label = val3; } else if (!_warnedNoFont) { _warnedNoFont = true; MelonLogger.Warning("[PvpCameraEsp] No TMP font found — boxes still draw, name labels disabled."); } _boxes[key] = value; return value; } catch { return null; } } private static RectTransform NewRect(string name, Transform parent, Vector2 aMin, Vector2 aMax, Vector2 pivot) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name); RectTransform val2 = val.AddComponent(); ((Transform)val2).SetParent(parent, false); val2.anchorMin = aMin; val2.anchorMax = aMax; val2.pivot = pivot; val2.anchoredPosition = Vector2.zero; return val2; } private static RawImage Edge(Transform parent, Vector2 aMin, Vector2 aMax, Vector2 pivot, Vector2 sizeDelta) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) RectTransform val = NewRect("edge", parent, aMin, aMax, pivot); val.sizeDelta = sizeDelta; RawImage val2 = ((Component)val).gameObject.AddComponent(); val2.texture = (Texture)(object)White; ((Graphic)val2).raycastTarget = false; return val2; } private static TMP_FontAsset ResolveFont() { if (_fontResolved) { return _font; } _fontResolved = true; try { Il2CppArrayBase val = Resources.FindObjectsOfTypeAll(); if (val != null) { foreach (TMP_FontAsset item in val) { if ((Object)(object)item != (Object)null && ((Object)item).name != null && ((Object)item).name.Contains("OpenSans")) { _font = item; return _font; } } foreach (TMP_FontAsset item2 in val) { if ((Object)(object)item2 != (Object)null && ((Object)item2).name != null && (((Object)item2).name.Contains("Inter") || ((Object)item2).name.Contains("Roboto") || ((Object)item2).name.Contains("Liberation") || ((Object)item2).name.Contains("Arial"))) { _font = item2; return _font; } } foreach (TMP_FontAsset item3 in val) { if ((Object)(object)item3 != (Object)null) { _font = item3; return _font; } } } } catch { } return _font; } internal static void Reset() { try { if ((Object)(object)_container != (Object)null) { Object.Destroy((Object)(object)((Component)_container).gameObject); } } catch { } _container = null; _bound = null; _boxes.Clear(); } } internal struct CamEntry { internal Camera Cam; internal string Name; internal CamEntry(Camera cam, string name) { Cam = cam; Name = name; } } internal static class NetEyeBridge { internal const string RtPrefix = "NetEye_RT_"; private const string RootPrefix = "NetEye_"; private static readonly List _all = new List(8); private static float _nextAllScan; private static int _lastCount = -1; private static Camera _feedCam; private static RawImage _feedCache; private static readonly Dictionary _niceCache = new Dictionary(); private static bool _reflectInit; private static bool _reflectOk; private static FieldInfo _fInstance; private static FieldInfo _fCameras; private static FieldInfo _fUnityCam; private static FieldInfo _fName; private static FieldInfo _fLoc; private static FieldInfo _fIsVanilla; private static FieldInfo _fActiveIdx; private static MethodInfo _mDestroy; internal static IReadOnlyList AllCameras() { float unscaledTime = Time.unscaledTime; if (unscaledTime >= _nextAllScan) { _nextAllScan = unscaledTime + 1f; RebuildAll(); } return _all; } private static void RebuildAll() { _all.Clear(); try { Il2CppArrayBase val = Resources.FindObjectsOfTypeAll(); if (val != null) { foreach (Camera item in val) { if (!((Object)(object)item == (Object)null)) { RenderTexture targetTexture = item.targetTexture; if (!((Object)(object)targetTexture == (Object)null) && ((Object)targetTexture).name != null && ((Object)targetTexture).name.StartsWith("NetEye_RT_")) { _all.Add(new CamEntry(item, ResolveName(item, targetTexture))); } } } } } catch { } if (EspConfig.DebugLog.Value && _all.Count != _lastCount) { _lastCount = _all.Count; MelonLogger.Msg("[PvpCameraEsp] NetEye cameras detected: " + _all.Count); } } internal static bool TryGetLive(out Camera cam, out RawImage feed, out string name) { cam = null; feed = null; name = null; try { IReadOnlyList readOnlyList = AllCameras(); for (int i = 0; i < readOnlyList.Count; i++) { Camera cam2 = readOnlyList[i].Cam; if ((Object)(object)cam2 != (Object)null && ((Behaviour)cam2).enabled) { cam = cam2; name = readOnlyList[i].Name; break; } } if ((Object)(object)cam == (Object)null) { return false; } feed = ResolveFeed(cam); return (Object)(object)feed != (Object)null; } catch { return false; } } private static RawImage ResolveFeed(Camera cam) { if ((Object)(object)_feedCache != (Object)null && (Object)(object)_feedCam == (Object)(object)cam && (Object)(object)_feedCache.texture == (Object)(object)cam.targetTexture) { return _feedCache; } _feedCache = null; _feedCam = cam; try { Texture targetTexture = (Texture)(object)cam.targetTexture; Il2CppArrayBase val = Resources.FindObjectsOfTypeAll(); if (val != null) { foreach (RawImage item in val) { if ((Object)(object)item == (Object)null || !((Object)(object)item.texture != (Object)null) || !((Object)(object)item.texture == (Object)(object)targetTexture)) { continue; } _feedCache = item; break; } } } catch { _feedCache = null; } return _feedCache; } private static string ResolveName(Camera cam, RenderTexture rt) { int instanceID = ((Object)cam).GetInstanceID(); if (_niceCache.TryGetValue(instanceID, out var value)) { return value; } string text = TryReflectName(cam); if (!string.IsNullOrEmpty(text)) { _niceCache[instanceID] = text; return text; } return GoName(cam, rt); } private static string GoName(Camera cam, RenderTexture rt) { try { Transform val = ((Component)cam).transform; while ((Object)(object)val.parent != (Object)null) { val = val.parent; } string name = ((Object)((Component)val).gameObject).name; if (!string.IsNullOrEmpty(name) && name.StartsWith("NetEye_")) { return name.Substring("NetEye_".Length); } if ((Object)(object)rt != (Object)null && ((Object)rt).name != null && ((Object)rt).name.StartsWith("NetEye_RT_")) { return ((Object)rt).name.Substring("NetEye_RT_".Length); } return string.IsNullOrEmpty(name) ? "?" : name; } catch { return "?"; } } internal static int RemoveStaleCameras() { int num = 0; try { EnsureReflect(); if (!_reflectOk || _fIsVanilla == null || _mDestroy == null || _fCameras == null) { return 0; } object value = _fInstance.GetValue(null); if (value == null) { return 0; } if (!(_fCameras.GetValue(value) is IList list)) { return 0; } for (int num2 = list.Count - 1; num2 >= 0; num2--) { object obj = list[num2]; if (obj == null) { continue; } bool flag; try { flag = (bool)_fIsVanilla.GetValue(obj); } catch { continue; } if (!flag) { try { _mDestroy.Invoke(obj, null); } catch { } try { list.RemoveAt(num2); num++; } catch { } } } if (num > 0 && _fActiveIdx != null) { try { int num3 = (int)_fActiveIdx.GetValue(value); if (num3 >= list.Count) { _fActiveIdx.SetValue(value, list.Count - 1); } } catch { } } } catch { } return num; } private static string TryReflectName(Camera cam) { try { EnsureReflect(); if (!_reflectOk) { return null; } object value = _fInstance.GetValue(null); if (value == null) { return null; } if (!(_fCameras.GetValue(value) is IEnumerable enumerable)) { return null; } foreach (object item in enumerable) { if (item == null) { continue; } object? value2 = _fUnityCam.GetValue(item); Camera val = (Camera)((value2 is Camera) ? value2 : null); if ((Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)cam) { continue; } string text = _fName.GetValue(item) as string; if (string.IsNullOrEmpty(text)) { return null; } string text2 = ((_fLoc != null) ? (_fLoc.GetValue(item) as string) : null); return string.IsNullOrEmpty(text2) ? text : (text + " (" + text2 + ")"); } } catch { } return null; } private static void EnsureReflect() { if (_reflectInit) { return; } _reflectInit = true; try { Type type = FindType("NetEye.NetEyeMod", "NetEye"); Type type2 = FindType("NetEye.NetEyeCamera", "NetEye"); if (!(type == null) && !(type2 == null)) { _fInstance = type.GetField("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _fCameras = type.GetField("_cameras", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _fActiveIdx = type.GetField("_activeCameraIdx", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _fUnityCam = type2.GetField("UnityCam", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _fName = type2.GetField("Name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _fLoc = type2.GetField("Location", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _fIsVanilla = type2.GetField("IsVanilla", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _mDestroy = type2.GetMethod("Destroy", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); _reflectOk = _fInstance != null && _fCameras != null && _fUnityCam != null && _fName != null; } } catch { _reflectOk = false; } } private static Type FindType(string fullName, string asmSimpleName) { try { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { if (assembly.GetName().Name == asmSimpleName) { Type type = assembly.GetType(fullName); if (type != null) { return type; } } } } catch { } try { return Type.GetType(fullName + ", " + asmSimpleName); } catch { return null; } } internal static void Reset() { _all.Clear(); _niceCache.Clear(); _feedCam = null; _feedCache = null; _nextAllScan = 0f; _lastCount = -1; } } public class PvpCameraEspMod : MelonMod { private float _nextSensorScan; private float _nextStaleScan; private bool _wasMatchActive; public override void OnInitializeMelon() { EspConfig.Load(); CameraGuard.Apply(); MelonLogger.Msg("[PvpCameraEsp] ready — symmetric camera ESP for MultiplayerPVP via NetEye cameras."); } public override void OnSceneWasLoaded(int buildIndex, string sceneName) { try { NetEyeBridge.Reset(); FeedOverlay.Reset(); SightingReporter.Reset(); CameraOwnership.Reset(); CameraBuildTracker.Reset(); _wasMatchActive = false; } catch (Exception ex) { MelonLogger.Warning("[PvpCameraEsp] OnSceneWasLoaded: " + ex.Message); } } public override void OnUpdate() { try { if (!EspConfig.Enabled.Value) { FeedOverlay.HideAll(); _wasMatchActive = false; return; } float unscaledTime = Time.unscaledTime; bool isHost = PvpApi.IsHost; bool isTeamsMatchActive = PvpApi.IsTeamsMatchActive; bool isMatchActive = PvpApi.IsMatchActive; CameraOwnership.Tick(EspConfig.OwnPlacementRadius.Value); if (isHost && EspConfig.CleanStaleCameras.Value && PvpApi.IsMultiplayer && unscaledTime >= _nextStaleScan) { _nextStaleScan = unscaledTime + 5f; int num = NetEyeBridge.RemoveStaleCameras(); if (num > 0 && EspConfig.DebugLog.Value) { MelonLogger.Msg($"[PvpCameraEsp] removed {num} stale camera(s)"); } } if (_wasMatchActive && !isMatchActive && EspConfig.CleanupOwnCamerasOnRoundEnd.Value) { CameraOwnership.QueuePickUpMine(); } else if (!_wasMatchActive && isMatchActive) { CameraOwnership.AbortPickup(); } _wasMatchActive = isMatchActive; CameraOwnership.TickPickupRetry(); if (!isTeamsMatchActive) { FeedOverlay.HideAll(); return; } if (unscaledTime >= _nextSensorScan) { _nextSensorScan = unscaledTime + 0.25f; IReadOnlyList readOnlyList = NetEyeBridge.AllCameras(); for (int i = 0; i < readOnlyList.Count; i++) { if (CameraOwnership.IsMineCamera(readOnlyList[i].Cam)) { List list = EnemyTracker.Scan(readOnlyList[i].Cam); if (list.Count > 0) { SightingReporter.Report(list, readOnlyList[i].Name); } } } } if (NetEyeBridge.TryGetLive(out var cam, out var feed, out var _) && CameraOwnership.IsMineCamera(cam)) { FeedOverlay.Update(feed, EnemyTracker.Scan(cam)); } else { FeedOverlay.HideAll(); } } catch (Exception ex) { MelonLogger.Warning("[PvpCameraEsp] OnUpdate: " + ex.Message); } } } internal static class SightingReporter { private static readonly Dictionary _lastSeen = new Dictionary(); private static readonly Dictionary _lastFired = new Dictionary(); internal static void Report(List enemies, string cameraName) { float unscaledTime = Time.unscaledTime; float num = Mathf.Max(0.5f, EspConfig.ReportCooldownSeconds.Value); float value = EspConfig.RemindWhileVisibleSeconds.Value; for (int i = 0; i < enemies.Count; i++) { VisibleEnemy visibleEnemy = enemies[i]; string text = ((visibleEnemy.SteamId != 0L) ? visibleEnemy.SteamId.ToString() : ("n:" + visibleEnemy.Persona)); string key = text + "@" + cameraName; float value2; bool flag = !_lastSeen.TryGetValue(key, out value2) || unscaledTime - value2 > num; float value3; bool flag2 = value > 0f && _lastFired.TryGetValue(key, out value3) && unscaledTime - value3 >= value; _lastSeen[key] = unscaledTime; if (flag || flag2) { _lastFired[key] = unscaledTime; if (EspConfig.DebugLog.Value) { MelonLogger.Msg($"[PvpCameraEsp] ALERT{((flag2 && !flag) ? " (reminder)" : "")}: {visibleEnemy.Persona} (team {visibleEnemy.Team}, steamId {visibleEnemy.SteamId}) @ {cameraName}"); } PvpApi.ReportCameraSighting(visibleEnemy.Team, visibleEnemy.SteamId, visibleEnemy.Persona, cameraName); } } if (_lastSeen.Count > 128) { Prune(unscaledTime, num); } } private static void Prune(float now, float rearm) { List list = new List(); foreach (KeyValuePair item in _lastSeen) { if (now - item.Value > rearm * 4f + 10f) { list.Add(item.Key); } } for (int i = 0; i < list.Count; i++) { _lastSeen.Remove(list[i]); _lastFired.Remove(list[i]); } } internal static void Reset() { try { _lastSeen.Clear(); _lastFired.Clear(); } catch { } } } }