using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using BepInEx; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Realtime; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("EnderGuy77")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0")] [assembly: AssemblyProduct("EnderGuy77")] [assembly: AssemblyTitle("Chained_Together")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.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 BepInEx { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class BepInAutoPluginAttribute : Attribute { public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace BepInEx.Preloader.Core.Patching { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class PatcherAutoPluginAttribute : Attribute { public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace Chained_Together { [BepInPlugin("EnderGuy77-SeparationTracker", "Separation Tracker (Photon-aware)", "1.9.1")] public class SeparationTracker : BaseUnityPlugin { private enum PlayerState { Alive, Dead, PassedOut, LoggedOff, Rejoining } [CompilerGenerated] private sealed class d__22 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public SeparationTracker <>4__this; public (Transform, Transform) pair; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__22(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) int num = <>1__state; SeparationTracker separationTracker = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(separationTracker.separationDelay); <>1__state = 1; return true; case 1: { <>1__state = -1; float num2 = Vector3.Distance(pair.Item1.position, pair.Item2.position); if (num2 > 70f) { separationTracker.separatedPairs.Add(pair); separationTracker.separationCount++; string text = $"Separation detected between {((Object)pair.Item1).name} and {((Object)pair.Item2).name} | Total: {separationTracker.separationCount}"; ((BaseUnityPlugin)separationTracker).Logger.LogInfo((object)text); File.AppendAllText(separationTracker.logFilePath, $"[{DateTime.Now:HH:mm:ss.fff}] {text}\n"); } return false; } } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private const float distanceThreshold = 70f; private float separationCooldown = 1.5f; private float separationDelay = 2.5f; private bool showGUI = true; private bool inGame; private List<(Transform, Transform)> separatedPairs = new List<(Transform, Transform)>(); private List players = new List(); private int separationCount; private float separationTimer; private string logFilePath = ""; private Dictionary actorToTransform = new Dictionary(); private HashSet activeActors = new HashSet(); private HashSet rejoiningActors = new HashSet(); private HashSet prevActorNumbers = new HashSet(); private PlayerState playerState; private void Awake() { SceneManager.activeSceneChanged += OnSceneChanged; CreateNewSessionFile(); UpdateNetworkPlayers(); } private void Update() { if (Input.GetKeyDown((KeyCode)291)) { showGUI = !showGUI; } if (Input.GetKeyDown((KeyCode)278) && !inGame) { SaveLogFile(); } UpdateNetworkPlayers(); UpdatePlayerState(); if (playerState == PlayerState.LoggedOff || playerState == PlayerState.PassedOut || playerState == PlayerState.Dead) { return; } if (playerState == PlayerState.Rejoining) { if (!CheckIfLocalPlayerCloseToActive()) { return; } playerState = PlayerState.Alive; } ActivateRejoiningRemotePlayers(); separationTimer += Time.deltaTime; if (separationTimer >= separationCooldown) { CheckSeparations(); separationTimer = 0f; } } private void UpdateNetworkPlayers() { try { Player[] playerList = PhotonNetwork.PlayerList; HashSet hashSet = new HashSet(playerList.Select((Player p) => p.ActorNumber)); foreach (int item in prevActorNumbers.Except(hashSet)) { if (actorToTransform.TryGetValue(item, out Transform t)) { RemovePairsWithTransform(t); actorToTransform.Remove(item); players.RemoveAll((Transform x) => (Object)(object)x == (Object)(object)t); } activeActors.Remove(item); rejoiningActors.Remove(item); } foreach (int item2 in hashSet.Except(prevActorNumbers)) { MapActorsToTransforms(); rejoiningActors.Add(item2); activeActors.Remove(item2); } MapActorsToTransforms(); prevActorNumbers = hashSet; } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"UpdateNetworkPlayers error: {arg}"); } } private void MapActorsToTransforms() { PhotonView[] array = Object.FindObjectsByType((FindObjectsSortMode)0); PhotonView[] array2 = array; foreach (PhotonView val in array2) { if (val.OwnerActorNr <= 0) { continue; } Transform transform = ((Component)val).transform; Transform val2 = transform.Find("Scout/Misc/CharacterLight"); if ((Object)(object)val2 == (Object)null) { continue; } int ownerActorNr = val.OwnerActorNr; if (!actorToTransform.ContainsKey(ownerActorNr) || (Object)(object)actorToTransform[ownerActorNr] == (Object)null) { actorToTransform[ownerActorNr] = val2; if (!players.Contains(val2)) { players.Add(val2); } } } List list = (from kv in actorToTransform where (Object)(object)kv.Value == (Object)null select kv.Key).ToList(); foreach (int item in list) { actorToTransform.Remove(item); activeActors.Remove(item); rejoiningActors.Remove(item); } } private void ActivateRejoiningRemotePlayers() { //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_0116: 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) if (rejoiningActors.Count == 0) { return; } List list = (from a in activeActors where actorToTransform.ContainsKey(a) select actorToTransform[a] into t where (Object)(object)t != (Object)null select t).ToList(); foreach (int item in rejoiningActors.ToList()) { if (!actorToTransform.TryGetValue(item, out Transform value) || (Object)(object)value == (Object)null) { continue; } bool flag = false; if (list.Count > 0) { foreach (Transform item2 in list) { if (Vector3.Distance(value.position, item2.position) < 70f) { flag = true; break; } } } else { foreach (Transform player in players) { if ((Object)(object)player != (Object)(object)value && Vector3.Distance(value.position, player.position) < 70f) { flag = true; break; } } } if (flag) { rejoiningActors.Remove(item); activeActors.Add(item); } } } private void CheckSeparations() { //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) players = actorToTransform.Values.Where((Transform t) => (Object)(object)t != (Object)null).ToList(); for (int i = 0; i < players.Count; i++) { for (int j = i + 1; j < players.Count; j++) { Transform p1 = players[i]; Transform p2 = players[j]; if ((Object)(object)p1 == (Object)null || (Object)(object)p2 == (Object)null) { continue; } int key = actorToTransform.FirstOrDefault>((KeyValuePair kv) => (Object)(object)kv.Value == (Object)(object)p1).Key; int key2 = actorToTransform.FirstOrDefault>((KeyValuePair kv) => (Object)(object)kv.Value == (Object)(object)p2).Key; if (!rejoiningActors.Contains(key) && !rejoiningActors.Contains(key2)) { float num = Vector3.Distance(p1.position, p2.position); (Transform, Transform) tuple = (p1, p2); (Transform, Transform) item = (p2, p1); if (num > 70f && !separatedPairs.Contains(tuple) && !separatedPairs.Contains(item)) { ((MonoBehaviour)this).StartCoroutine(CountSeparationWithDelay(tuple)); } if (num < 70f && (separatedPairs.Contains(tuple) || separatedPairs.Contains(item))) { separatedPairs.Remove(tuple); separatedPairs.Remove(item); } } } } } [IteratorStateMachine(typeof(d__22))] private IEnumerator CountSeparationWithDelay((Transform, Transform) pair) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__22(0) { <>4__this = this, pair = pair }; } private void RemovePairsWithTransform(Transform t) { Transform t2 = t; separatedPairs.RemoveAll(((Transform, Transform) p) => (Object)(object)p.Item1 == (Object)(object)t2 || (Object)(object)p.Item2 == (Object)(object)t2); } private bool CheckIfLocalPlayerCloseToActive() { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) try { Character localCharacter = Character.localCharacter; Transform local = ((localCharacter != null) ? ((Component)localCharacter).transform : null); if ((Object)(object)local == (Object)null) { return false; } List list = (from a in activeActors where actorToTransform.ContainsKey(a) select actorToTransform[a] into t where (Object)(object)t != (Object)null && (Object)(object)t != (Object)(object)local select t).ToList(); foreach (Transform item in list) { if (Vector3.Distance(local.position, item.position) < 70f) { return true; } } List list2 = players.Where((Transform p) => (Object)(object)p != (Object)null && (Object)(object)p != (Object)(object)local).ToList(); foreach (Transform item2 in list2) { if (Vector3.Distance(local.position, item2.position) < 70f) { return true; } } } catch { } return false; } private void UpdatePlayerState() { try { if (!PhotonNetwork.IsConnected) { playerState = PlayerState.LoggedOff; return; } Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter == (Object)null) { playerState = PlayerState.LoggedOff; return; } CharacterData data = localCharacter.data; if (playerState == PlayerState.LoggedOff && PhotonNetwork.LocalPlayer != null) { playerState = PlayerState.Rejoining; int actorNumber = PhotonNetwork.LocalPlayer.ActorNumber; rejoiningActors.Add(actorNumber); activeActors.Remove(actorNumber); } if (data.dead) { playerState = PlayerState.Dead; } else if (data.passedOut) { playerState = PlayerState.PassedOut; } else if (playerState != PlayerState.Rejoining) { playerState = PlayerState.Alive; } } catch { playerState = PlayerState.LoggedOff; } } private void OnGUI() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) if (showGUI) { float num = 350f; float num2 = ((float)Screen.width - num / 2f) / 2f; GUI.Label(new Rect(num2, 30f, num, 25f), $"Total Separations: {separationCount}"); GUI.Label(new Rect(num2, 45f, num, 25f), "To hide this UI use F10"); GUI.Label(new Rect(num2, 60f, num, 25f), "To save logs use Home"); } } private string ComputeHash(string input) { using SHA256 sHA = SHA256.Create(); byte[] bytes = Encoding.UTF8.GetBytes(input); byte[] inArray = sHA.ComputeHash(bytes); return Convert.ToBase64String(inArray); } private void OnSceneChanged(Scene newScene, Scene oldScene) { inGame = ((Scene)(ref newScene)).name != "Title"; } private void SaveLogFile() { try { string text = $"--- Separation Log ({DateTime.Now}) ---\n"; text += $"Total Separations: {separationCount}\n"; string text2 = ComputeHash(text); text = text + "Hash: " + text2 + "\n\n"; Directory.CreateDirectory(Path.GetDirectoryName(logFilePath)); File.AppendAllText(logFilePath, text); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Separation data manually saved (F9)."); separationCount = 0; separatedPairs.Clear(); } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"Failed to save log file: {arg}"); } } private void CreateNewSessionFile() { string path = "SeparationLog.txt"; logFilePath = Path.Combine(Paths.BepInExRootPath, path); File.WriteAllText(logFilePath, $"Session started at {DateTime.Now}\n"); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Created new session log file: " + logFilePath)); } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }