Decompiled source of Chained Together v1.1.1

EnderGuy77-Chained_Together.dll

Decompiled 7 months ago
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 <CountSeparationWithDelay>d__22 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public SeparationTracker <>4__this;

			public (Transform, Transform) pair;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <CountSeparationWithDelay>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<Transform> players = new List<Transform>();

		private int separationCount;

		private float separationTimer;

		private string logFilePath = "";

		private Dictionary<int, Transform> actorToTransform = new Dictionary<int, Transform>();

		private HashSet<int> activeActors = new HashSet<int>();

		private HashSet<int> rejoiningActors = new HashSet<int>();

		private HashSet<int> prevActorNumbers = new HashSet<int>();

		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<int> hashSet = new HashSet<int>(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<PhotonView>((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<int> 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<Transform> 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<int, Transform>>((KeyValuePair<int, Transform> kv) => (Object)(object)kv.Value == (Object)(object)p1).Key;
					int key2 = actorToTransform.FirstOrDefault<KeyValuePair<int, Transform>>((KeyValuePair<int, Transform> 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(<CountSeparationWithDelay>d__22))]
		private IEnumerator CountSeparationWithDelay((Transform, Transform) pair)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <CountSeparationWithDelay>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<Transform> 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<Transform> 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)
		{
		}
	}
}