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)
{
}
}
}