Decompiled source of Entanglement Redux v2.1.0

EntanglementRedux.dll

Decompiled 3 weeks ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.IO.Pipes;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml.Linq;
using Boneworks;
using Entanglement;
using Entanglement.Compat;
using Entanglement.Compat.CustomMaps;
using Entanglement.Compat.Playermodels;
using Entanglement.Data;
using Entanglement.Exceptions;
using Entanglement.Extensions;
using Entanglement.Managers;
using Entanglement.Modularity;
using Entanglement.Network;
using Entanglement.Objects;
using Entanglement.Patching;
using Entanglement.Representation;
using Entanglement.Sync;
using Entanglement.UI;
using Entanglement.Voice;
using HarmonyLib;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Il2CppSystem.Reflection;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.Win32;
using Microsoft.Win32.SafeHandles;
using ModThatIsNotMod;
using ModThatIsNotMod.BoneMenu;
using PuppetMasta;
using Steamworks;
using StressLevelZero;
using StressLevelZero.AI;
using StressLevelZero.Arena;
using StressLevelZero.Combat;
using StressLevelZero.Data;
using StressLevelZero.Environment;
using StressLevelZero.Interaction;
using StressLevelZero.Player;
using StressLevelZero.Pool;
using StressLevelZero.Props;
using StressLevelZero.Props.Weapons;
using StressLevelZero.Rig;
using StressLevelZero.SFX;
using StressLevelZero.UI;
using StressLevelZero.UI.Radial;
using StressLevelZero.Utilities;
using StressLevelZero.VRMK;
using StressLevelZero.Zones;
using TMPro;
using UnhollowerBaseLib;
using UnhollowerBaseLib.Runtime;
using UnhollowerRuntimeLib;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Audio;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: Guid("490e160d-251d-4ab4-a3bb-f473961ff8a1")]
[assembly: AssemblyTitle("Entanglement Redux")]
[assembly: AssemblyFileVersion("2.1.0")]
[assembly: MelonInfo(typeof(EntanglementMod), "Entanglement Redux", "2.1.0", "willpsdk", null)]
[assembly: MelonGame("Stress Level Zero", "BONEWORKS")]
[assembly: MelonIncompatibleAssemblies(new string[] { "MultiplayerMod" })]
[assembly: MelonPriority(-10000)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.1.0.0")]
[module: UnverifiableCode]
namespace Entanglement
{
	public static class EntangleLogger
	{
		public static bool verbose;

		private static Instance Logger
		{
			get
			{
				try
				{
					EntanglementMod instance = EntanglementMod.Instance;
					if (instance != null)
					{
						return ((MelonBase)instance).LoggerInstance;
					}
				}
				catch
				{
				}
				return null;
			}
		}

		public static void Log(string txt, ConsoleColor txt_color = ConsoleColor.White)
		{
			Instance logger = Logger;
			if (logger != null)
			{
				logger.Msg(txt_color, txt);
			}
			else
			{
				MelonLogger.Msg(txt_color, "[Entanglement] " + txt);
			}
		}

		public static void Log(object obj, ConsoleColor txt_color = ConsoleColor.White)
		{
			Instance logger = Logger;
			if (logger != null)
			{
				logger.Msg(txt_color, obj);
			}
			else
			{
				MelonLogger.Msg(txt_color, "[Entanglement] " + obj);
			}
		}

		public static void Debug(string txt, ConsoleColor txt_color = ConsoleColor.White)
		{
			if (verbose)
			{
				Log(txt, txt_color);
			}
		}

		public static void Warn(string txt)
		{
			Instance logger = Logger;
			if (logger != null)
			{
				logger.Warning(txt);
			}
			else
			{
				MelonLogger.Warning("[Entanglement] " + txt);
			}
		}

		public static void Warn(object obj)
		{
			Instance logger = Logger;
			if (logger != null)
			{
				logger.Warning(obj);
			}
			else
			{
				MelonLogger.Warning("[Entanglement] " + obj);
			}
		}

		public static void Error(string txt)
		{
			Instance logger = Logger;
			if (logger != null)
			{
				logger.Error(txt);
			}
			else
			{
				MelonLogger.Error("[Entanglement] " + txt);
			}
		}

		public static void Error(object obj)
		{
			Instance logger = Logger;
			if (logger != null)
			{
				logger.Error(obj);
			}
			else
			{
				MelonLogger.Error("[Entanglement] " + obj);
			}
		}
	}
	public static class DebugLog
	{
		private static MelonPreferences_Category category;

		private static MelonPreferences_Entry<bool> saved;

		private static bool Saved
		{
			get
			{
				try
				{
					if (saved == null)
					{
						category = MelonPreferences.CreateCategory("EntanglementDebug");
						saved = category.CreateEntry<bool>("debugLog", false, (string)null, "Detailed Entanglement logging, the same as the Representation Test's, without the dummy", false, false, (ValueValidator)null, (string)null);
					}
					return saved.Value;
				}
				catch
				{
					return false;
				}
			}
		}

		public static bool On => Saved;

		public static void Initialize()
		{
			Apply();
			if (On)
			{
				EntangleLogger.Log("[Debug] Debug log is on (Settings > Debug to turn it off).", ConsoleColor.DarkYellow);
			}
		}

		public static void Toggle()
		{
			bool flag = !Saved;
			try
			{
				saved.Value = flag;
				MelonPreferences.Save();
			}
			catch (Exception ex)
			{
				EntangleLogger.Warn("[Debug] Could not save the debug log setting: " + ex.Message);
			}
			Apply();
			if (flag)
			{
				EntangleLogger.Log("[Debug] Debug log on.", ConsoleColor.DarkYellow);
				EntangleLogger.Log("[Debug] " + Snapshot(), ConsoleColor.DarkYellow);
			}
			else
			{
				EntangleLogger.Log("[Debug] Debug log off" + (PlayerRepresentation.RepresentationTestActive ? " (the Representation Test keeps it on until it is stopped)." : "."), ConsoleColor.DarkYellow);
			}
		}

		public static void Apply()
		{
			bool flag = (EntangleLogger.verbose = Saved || PlayerRepresentation.RepresentationTestActive);
			try
			{
				NpcTrace.enabled = flag;
			}
			catch
			{
			}
			try
			{
				BossClawSync.traceEnabled = flag;
			}
			catch
			{
			}
			try
			{
				SceneEventSync.traceEnabled = flag;
			}
			catch
			{
			}
		}

		public static string Snapshot()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				string text = ((!SteamIntegration.hasLobby) ? "no lobby" : (Node.isServer ? "host" : "client"));
				string text2;
				try
				{
					Scene activeScene = SceneManager.GetActiveScene();
					text2 = ((Scene)(ref activeScene)).name;
				}
				catch
				{
					text2 = "?";
				}
				return "Entanglement " + EntanglementMod.VersionString + " (build " + EntanglementVersion.BuildTag + "), " + text + ", " + PlayerRepresentation.representations.Count + " other player(s), scene " + text2 + ", slow-mo " + SlowMoSync.Mode.ToString() + ", holstered items pinned " + HolsterSync.PinnedCount + ", buttons held for others " + ButtonTogglePatch.RemoteHeldCount + ", gun slides shown for others " + GunSlideSync.MirrorCount + ", Discord " + (DiscordInvite.Connected ? "connected" : "not connected") + ((DiscordInvite.RivalApp != null) ? (" (" + DiscordInvite.RivalApp + " running)") : "") + ". " + NetStats.StateLine();
			}
			catch (Exception ex)
			{
				return "snapshot unavailable: " + ex.Message;
			}
		}
	}
	public static class EntangleNotif
	{
		public static void PlayerJoin(string username)
		{
			Notifications.SendNotification(username + " has joined!", 4f);
		}

		public static void PlayerLeave(string username)
		{
			Notifications.SendNotification(username + " left.", 4f);
		}

		public static void PlayerDisconnect(DisconnectReason reason, string additionalReason = "")
		{
			if (reason == DisconnectReason.MissingMods && !string.IsNullOrEmpty(additionalReason))
			{
				Notifications.SendNotification("You are missing mods:\n" + additionalReason, 8f);
			}
			else if (!string.IsNullOrEmpty(additionalReason))
			{
				Notifications.SendNotification($"Disconnected: {reason}\n{additionalReason}", 6f);
			}
			else
			{
				Notifications.SendNotification($"Disconnected: {reason}", 4f);
			}
		}

		public static void MissingMods(string missingMods)
		{
			Notifications.SendNotification("Cannot join, you are missing mods:\n" + missingMods, 8f);
		}

		public static void LobbyStarted()
		{
			Notifications.SendNotification("Lobby has been created!", 4f);
		}

		public static void JoinServer(string username)
		{
			Notifications.SendNotification("Joined " + username + "'s game.", 4f);
		}

		public static void LeftServer()
		{
			Notifications.SendNotification("You have left", 4f);
		}

		public static void InvalidSteam()
		{
			Notifications.SendNotification("Steam did not start up, so Entanglement is off.\nCheck Steam is running and launch the game through it.", 4f);
		}
	}
	[StructLayout(LayoutKind.Sequential, Size = 1)]
	public struct EntanglementVersion
	{
		public const byte versionMajor = 2;

		public const byte versionMinor = 1;

		public const short versionPatch = 0;

		public const byte minVersionMajorSupported = 2;

		public const byte minVersionMinorSupported = 1;

		public const byte protocolRevision = 19;

		private static ulong buildId;

		public static ulong BuildId
		{
			get
			{
				if (buildId == 0L)
				{
					try
					{
						buildId = BitConverter.ToUInt64(typeof(EntanglementMod).Module.ModuleVersionId.ToByteArray(), 0);
					}
					catch
					{
						buildId = 1uL;
					}
				}
				return buildId;
			}
		}

		public static string BuildTag => (byte)19 + ":" + BuildId.ToString("x16");

		public static bool TryParseBuildTag(string tag, out byte revision, out ulong build)
		{
			revision = 0;
			build = 0uL;
			if (string.IsNullOrEmpty(tag))
			{
				return false;
			}
			int num = tag.IndexOf(':');
			if (num <= 0)
			{
				return false;
			}
			if (byte.TryParse(tag.Substring(0, num), out revision))
			{
				return ulong.TryParse(tag.Substring(num + 1), NumberStyles.HexNumber, null, out build);
			}
			return false;
		}
	}
	public class EntanglementMod : MelonMod
	{
		public static byte? sceneChange;

		public static bool hostLoading;

		public static Assembly entanglementAssembly;

		public static bool hasUnpatched;

		private static int menuWait;

		private static bool uiUnlocked;

		private static float lastUpdateRealtime;

		private const float SUSPEND_GAP_SECONDS = 10f;

		private static readonly Dictionary<string, int> tickFailures;

		private static int loadsWhileWaiting;

		public static EntanglementMod Instance { get; protected set; }

		public static string VersionString { get; protected set; }

		static EntanglementMod()
		{
			sceneChange = null;
			hasUnpatched = false;
			menuWait = -1;
			lastUpdateRealtime = 0f;
			tickFailures = new Dictionary<string, int>();
			SteamworksEmbed.HookResolve();
		}

		public override void OnApplicationStart()
		{
			entanglementAssembly = Assembly.GetExecutingAssembly();
			Instance = this;
			VersionString = $"{(byte)2}.{(byte)1}.{(short)0}";
			EntangleLogger.Log("Current Entanglement version is " + VersionString + " (build " + EntanglementVersion.BuildTag + ")");
			EntangleLogger.Log($"Minimum supported Entanglement version is {(byte)2}.{(byte)1}.*");
			DebugLog.Initialize();
			PersistentData.Initialize();
			SteamworksEmbed.EnsureSteamworksOnDisk();
			GameSDK.LoadGameSDK();
			SteamIntegration.Initialize();
			if (SteamIntegration.isInvalid)
			{
				EntangleNotif.InvalidSteam();
				return;
			}
			Patcher.Initialize();
			try
			{
				int num = 0;
				foreach (MethodBase patchedMethod in ((MelonBase)this).HarmonyInstance.GetPatchedMethods())
				{
					_ = patchedMethod;
					num++;
				}
				EntangleLogger.Log($"Patched {num} game methods");
			}
			catch
			{
			}
			NetworkMessage.RegisterHandlersFromAssembly(entanglementAssembly);
			Client.StartClient();
			CustomItemSync.Initialize();
			PlayermodelSync.Initialize();
			MelonCoroutines.Start(CoDeferredBoot());
			BanList.PullFromFile();
			EntangleLogger.Log("Welcome to the Entanglement Redux Beta!", ConsoleColor.DarkYellow);
			UpdateCheck.Start();
		}

		private static IEnumerator CoDeferredBoot()
		{
			EntangleLogger.Log("Waiting for Unity before loading embedded bundles...");
			for (int i = 0; i < 60; i++)
			{
				yield return null;
			}
			try
			{
				EntangleLogger.Log("Loading playerrep bundle...");
				PlayerRepresentation.LoadBundle();
				EntangleLogger.Log("playerrep ready.");
			}
			catch (Exception ex)
			{
				EntangleLogger.Error("playerrep bundle failed: " + ex);
			}
			try
			{
				LoadingScreen.LoadBundle();
				EntangleLogger.Log("logo bundle ready.");
			}
			catch (Exception ex2)
			{
				EntangleLogger.Error("logo bundle failed: " + ex2);
			}
		}

		public override void OnApplicationLateStart()
		{
			if (SteamIntegration.isInvalid)
			{
				((MelonBase)this).HarmonyInstance.UnpatchSelf();
				hasUnpatched = true;
			}
		}

		public override void OnUpdate()
		{
			if (SteamIntegration.isInvalid)
			{
				if (!hasUnpatched)
				{
					((MelonBase)this).HarmonyInstance.UnpatchSelf();
					hasUnpatched = true;
				}
				return;
			}
			NetStats.BeginFrame();
			HitSource.BeginFrame();
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (lastUpdateRealtime > 0f && realtimeSinceStartup - lastUpdateRealtime > 10f)
			{
				EntangleLogger.Debug($"App was suspended for {realtimeSinceStartup - lastUpdateRealtime:F1}s, draining the stale network backlog...");
				Node.activeNode?.ClearMessageBuffer();
			}
			lastUpdateRealtime = realtimeSinceStartup;
			SafeTick("TransformSyncBatcher", TransformSyncBatcher.Flush);
			SafeTick("VoiceManager", VoiceManager.Tick);
			SafeTick("LargePropSync", LargePropSync.Tick);
			SafeTick("SpawnCatchUp", SpawnCatchUp.Tick);
			SafeTick("NpcDespawnSync", NpcDespawnSync.Tick);
			SafeTick("NpcOwnerWatch", NpcOwnerWatch.Tick);
			SafeTick("RemoteZonePresence", RemoteZonePresence.Tick);
			SafeTick("RepTargetEmulator", RepTargetEmulator.Tick);
			SafeTick("SceneNpcSync", SceneNpcSync.Tick);
			SafeTick("NpcConfigSync", NpcConfigSync.Tick);
			SafeTick("TurretSync", TurretSync.Tick);
			SafeTick("ModuleHandler", ModuleHandler.Update);
			SafeTick("NetStats", NetStats.Tick);
			SafeTick("PhysicsCatchUp", PhysicsCatchUp.Tick);
			SafeTick("DownloadUI", DownloadUI.Update);
			if (!uiUnlocked)
			{
				if (menuWait > 0)
				{
					menuWait--;
				}
				else if (menuWait == 0)
				{
					uiUnlocked = true;
					menuWait = -1;
				}
			}
			if (uiUnlocked)
			{
				SafeTick("EntanglementRadial", EntanglementRadial.Tick);
				SafeTick("GameRadial", GameRadial.Tick);
				SafeTick("GameNotify", GameNotify.Tick);
				SafeTick("UpdateCheck", UpdateCheck.Tick);
			}
			SafeTick("SyncPlayerReps", PlayerRepresentation.SyncPlayerReps);
			SafeTick("GripRadius", GripRadiusPatch.Flush);
			SafeTick("UpdatePlayerReps.Frame", PlayerRepresentation.UpdatePlayerRepsPerFrame);
			SafeTick("FileTransfer", FileTransferManager.Tick);
			SafeTick("Thunderstore", Thunderstore.Tick);
			SafeTick("DownloadGate", DownloadGate.Tick);
			SafeTick("SlowMo", SlowMoSync.Tick);
			SafeTick("Lethality", PlayerDeathManager.CheckLethality);
			SafeTick("HeldGripGuard", HeldGripGuard.Tick);
			SafeTick("CorpseCleanup", CorpseCleanup.Tick);
			SafeTick("NpcPoolGuard", NpcPoolGuard.Tick);
			SafeTick("LocalZoneWatch", LocalZoneWatch.Tick);
			SafeTick("BossClawSync", BossClawSync.Tick);
			SafeTick("ManipulatorSync", ManipulatorSync.Tick);
			SafeTick("RepCollisionGuard", RepCollisionGuard.Tick);
		}

		private static void SafeTick(string name, Action tick)
		{
			long timestamp = Stopwatch.GetTimestamp();
			try
			{
				tick();
			}
			catch (Exception ex)
			{
				tickFailures.TryGetValue(name, out var value);
				value++;
				tickFailures[name] = value;
				if (value <= 3 || value % 600 == 0)
				{
					EntangleLogger.Warn("[Tick] " + name + " failed (" + value + "x): " + ex.Message);
				}
			}
			NetStats.AddCost(name, Stopwatch.GetTimestamp() - timestamp);
		}

		public override void OnFixedUpdate()
		{
			if (!SteamIntegration.isInvalid)
			{
				NetStats.MarkFixed();
				SafeTick("ModuleHandler.Fixed", ModuleHandler.FixedUpdate);
				SafeTick("UpdatePlayerReps", PlayerRepresentation.UpdatePlayerReps);
				SafeTick("DriveRepPhysics", PlayerRepresentation.DriveRepPhysics);
				SafeTick("SoloDebug", SoloDebug.FixedTick);
			}
		}

		public override void OnLateUpdate()
		{
			if (!SteamIntegration.isInvalid)
			{
				SafeTick("ModuleHandler.Late", ModuleHandler.LateUpdate);
				SafeTick("Client.Tick", delegate
				{
					Client.instance?.Tick();
				});
				SafeTick("Server.Tick", delegate
				{
					Server.instance?.Tick();
				});
				SafeTick("SteamIntegration", SteamIntegration.Tick);
				SafeTick("PresentPlayerReps", PlayerRepresentation.PresentPlayerReps);
				SafeTick("HolsterSync", HolsterSync.Tick);
				SafeTick("SlowMo.Apply", SlowMoSync.Apply);
				SafeTick("GunSlide", GunSlideSync.Tick);
				NetStats.MarkLateEnd();
			}
		}

		public override void OnSceneWasInitialized(int buildIndex, string sceneName)
		{
			if (SteamIntegration.isInvalid)
			{
				return;
			}
			Application.backgroundLoadingPriority = (ThreadPriority)1;
			QualitySettings.asyncUploadTimeSlice = 2;
			QualitySettings.asyncUploadBufferSize = 4;
			if (EntangleLogger.verbose)
			{
				EntangleLogger.Debug("[Scene] ---- " + sceneName + " (" + buildIndex + ") loaded ---- " + DebugLog.Snapshot());
			}
			ModuleHandler.OnSceneWasInitialized(buildIndex, sceneName);
			if (SteamIntegration.hasLobby)
			{
				SpawnableData.EnsureLobbyUnlocks();
			}
			else
			{
				SpawnableData.GetData();
			}
			PlayerScripts.GetPlayerScripts();
			try
			{
				PlayerDeathManager.Initialize();
			}
			catch (Exception ex)
			{
				EntangleLogger.Warn("PlayerDeathManager init: " + ex.Message);
			}
			CorpseCleanup.Clear();
			TurretSync.OnSceneCleanup();
			ManipulatorSync.OnSceneCleanup();
			TurretSight.OnSceneCleanup();
			LargePropSync.OnSceneCleanup();
			BossClawSync.OnSceneCleanup();
			RepCollisionGuard.OnSceneCleanup();
			TransformMirrorPatch.OnSceneCleanup();
			HeldGripGuard.OnSceneCleanup();
			ZoneTrackingUtilities.OnSceneCleanup();
			InstantiatePatch.OnSceneCleanup();
			CustomItemSync.OnSceneCleanup();
			SpawnCatchUp.OnSceneCleanup();
			BreakSync.OnSceneCleanup();
			NpcTrace.OnSceneCleanup();
			RemoteZonePresence.OnSceneCleanup();
			LocalZoneWatch.OnSceneCleanup();
			NpcDespawnSync.OnSceneCleanup();
			NpcConfigSync.OnSceneCleanup();
			NpcPoolGuard.OnSceneCleanup();
			RepTargetEmulator.OnSceneCleanup();
			SoloDebug.OnSceneCleanup();
			StoryInteractSync.OnSceneCleanup();
			SlowMoSync.OnSceneLoaded();
			SceneEventSync.OnSceneReady();
			try
			{
				BreakPieces.CaptureHomes();
			}
			catch (Exception ex2)
			{
				EntangleLogger.Warn("BreakPieces.CaptureHomes: " + ex2.Message);
			}
			PlayerRepresentation.GetPlayerTransforms();
			menuWait = 180;
			uiUnlocked = false;
			EntanglementRadial.OnSceneWasInitialized();
			GameRadial.OnSceneWasInitialized();
			DownloadUI.OnSceneWasInitialized();
			GameNotify.OnSceneWasInitialized();
			foreach (PlayerRepresentation value in PlayerRepresentation.representations.Values)
			{
				value.RecreateRepresentations();
			}
			Client.instance.currentScene = (byte)buildIndex;
			bool num = Client.instance.pendingScene >= 0 && Client.instance.pendingScene != buildIndex;
			bool flag = Node.isServer && LevelChangeAnnouncer.levelAnnounced && LevelChangeAnnouncer.lastAnnouncedIndex != buildIndex;
			bool flag2 = num || flag;
			if (flag2)
			{
				loadsWhileWaiting++;
				if (loadsWhileWaiting >= 2)
				{
					EntangleLogger.Warn("[Scene] Still waiting for scene " + Client.instance.pendingScene + " after " + loadsWhileWaiting + " loads; syncing to " + buildIndex + " instead.");
					Client.instance.pendingScene = -1;
					LevelChangeAnnouncer.levelAnnounced = false;
					flag2 = false;
				}
			}
			else
			{
				loadsWhileWaiting = 0;
			}
			if (!flag2)
			{
				Client.instance.pendingScene = -1;
				ClientSceneGate.Clear();
				hostLoading = false;
				if (!LevelChangeAnnouncer.ConsumeAnnounce(buildIndex) && !CustomMapSync.HasCurrentMap)
				{
					sceneChange = (byte)buildIndex;
				}
				if (SteamIntegration.hasLobby && !Node.isServer && Node.activeNode != null)
				{
					NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.ClientReady, new EmptyMessageData());
					if (networkMessage != null)
					{
						Node.activeNode.BroadcastMessage(NetworkChannel.Reliable, networkMessage.GetBytes());
					}
				}
			}
			Client.TryCommandLineJoin();
			SteamIntegration.targetScene = sceneName.ToLower();
			SteamIntegration.UpdateActivity();
			if (SteamIntegration.hasLobby)
			{
				TurretSync.OnSceneReady();
				NpcPoolGuard.OnSceneReady();
				LargePropSync.OnSceneReady();
			}
		}

		public override void BONEWORKS_OnLoadingScreen()
		{
			if (!SteamIntegration.isInvalid)
			{
				Application.backgroundLoadingPriority = (ThreadPriority)4;
				QualitySettings.asyncUploadTimeSlice = 8;
				QualitySettings.asyncUploadBufferSize = 16;
				ModuleHandler.OnLoadingScreen();
				hostLoading = SteamIntegration.hasLobby && Node.isServer;
				LoadingScreen.OverrideScreen();
				ObjectSync.OnCleanup();
				ObjectSync.poolPairs.Clear();
				SceneEventSync.OnSceneCleanup();
				TurretSync.OnSceneCleanup();
				ManipulatorSync.OnSceneCleanup();
				TurretSight.OnSceneCleanup();
				LargePropSync.OnSceneCleanup();
				BossClawSync.OnSceneCleanup();
				RepCollisionGuard.OnSceneCleanup();
				TransformMirrorPatch.OnSceneCleanup();
				HeldGripGuard.OnSceneCleanup();
				ZoneTrackingUtilities.OnSceneCleanup();
				InstantiatePatch.OnSceneCleanup();
				CustomItemSync.OnSceneCleanup();
				SpawnCatchUp.OnSceneCleanup();
				BreakSync.OnSceneCleanup();
				NpcTrace.OnSceneCleanup();
				RemoteZonePresence.OnSceneCleanup();
				LocalZoneWatch.OnSceneCleanup();
				NpcDespawnSync.OnSceneCleanup();
				NpcConfigSync.OnSceneCleanup();
				NpcPoolGuard.OnSceneCleanup();
				RepTargetEmulator.OnSceneCleanup();
				SoloDebug.OnSceneCleanup();
				StoryInteractSync.OnSceneCleanup();
				TriggerRelay.OnSceneCleanup();
				SceneNpcSync.OnSceneCleanup();
				PooleeSyncable.OnSceneCleanup();
				NpcPending.OnSceneCleanup();
				NpcOwnerWatch.OnSceneCleanup();
				TransformSyncBatcher.Clear();
				Server.instance?.replayedUsers.Clear();
			}
		}

		public override void OnApplicationQuit()
		{
			if (!SteamIntegration.isInvalid)
			{
				ModuleHandler.OnApplicationQuit();
				Node.activeNode.Shutdown();
				SteamIntegration.Shutdown();
			}
		}
	}
	public static class UpdateCheck
	{
		private const string packageUrl = "https://thunderstore.io/api/experimental/package/willpsdk/Entanglement_Redux/";

		private static string pendingMessage;

		private static int started;

		public static void Start()
		{
			if (Interlocked.Exchange(ref started, 1) == 0)
			{
				Thread thread = new Thread(Run);
				thread.IsBackground = true;
				thread.Name = "EntanglementUpdateCheck";
				thread.Start();
			}
		}

		public static void Tick()
		{
			string text = Interlocked.Exchange(ref pendingMessage, null);
			if (!string.IsNullOrEmpty(text))
			{
				GameNotify.Show(text, 8f);
			}
		}

		private static void Run()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: 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)
			try
			{
				HttpClientHandler val = new HttpClientHandler
				{
					AutomaticDecompression = (DecompressionMethods.GZip | DecompressionMethods.Deflate)
				};
				HttpClient val2 = new HttpClient((HttpMessageHandler)val)
				{
					Timeout = TimeSpan.FromSeconds(8.0)
				};
				string result = val2.GetStringAsync("https://thunderstore.io/api/experimental/package/willpsdk/Entanglement_Redux/").GetAwaiter().GetResult();
				((HttpMessageInvoker)val2).Dispose();
				((HttpMessageHandler)val).Dispose();
				string text = ReadLatestVersion(result);
				if (string.IsNullOrEmpty(text))
				{
					EntangleLogger.Warn("Thunderstore didn't return a version for Entanglement Redux.");
					return;
				}
				if (!Version.TryParse(EntanglementMod.VersionString, out Version result2) || !Version.TryParse(text, out Version result3))
				{
					EntangleLogger.Warn("Couldn't compare versions (yours " + EntanglementMod.VersionString + ", Thunderstore " + text + ").");
					return;
				}
				int num = result2.CompareTo(result3);
				if (num < 0)
				{
					EntangleLogger.Warn($"Entanglement Redux is out of date. Yours: {result2}. Latest: {result3}.");
					pendingMessage = $"Entanglement Redux is out of date. Yours: {result2}. Latest: {result3}.";
				}
				else if (num == 0)
				{
					EntangleLogger.Log($"You're on the latest Entanglement Redux ({result2}).");
				}
				else
				{
					EntangleLogger.Log($"This build ({result2}) is newer than Thunderstore ({result3}).");
				}
			}
			catch (Exception ex)
			{
				EntangleLogger.Warn("Couldn't reach Thunderstore to check for an update (" + ex.GetType().Name + ").");
			}
		}

		private static string ReadLatestVersion(string json)
		{
			if (string.IsNullOrEmpty(json))
			{
				return null;
			}
			int num = json.IndexOf("\"latest\"", StringComparison.Ordinal);
			if (num < 0)
			{
				return null;
			}
			int num2 = json.IndexOf("\"version_number\":\"", num, StringComparison.Ordinal);
			if (num2 < 0)
			{
				return null;
			}
			num2 += "\"version_number\":\"".Length;
			int num3 = json.IndexOf('"', num2);
			if (num3 < 0)
			{
				return null;
			}
			return json.Substring(num2, num3 - num2);
		}
	}
}
namespace Entanglement.Voice
{
	public enum VoiceMode : byte
	{
		Proximity,
		Global
	}
	public static class VoiceManager
	{
		private class VoicePlayer
		{
			public AudioSource source;

			public AudioLowPassFilter lowPass;

			public Transform head;

			public AudioClip clip;

			public int clipSamples;

			public long written;

			public long played;

			public int lastTimeSamples;

			public float lastReceiveTime;

			public float targetVolume = 1f;

			public float targetCutoff = 22000f;

			public float volume = 1f;

			public float cutoff = 22000f;

			public float nextOcclusionCheck;

			public float occlusion;
		}

		public static bool micEnabled = true;

		public static VoiceMode mode = VoiceMode.Proximity;

		public static int proximityRange = 30;

		public static int outputVolume = 100;

		private const float baseVoiceGain = 2f;

		private static bool recording;

		private static uint sampleRate;

		private static readonly byte[] compressedBuffer = new byte[8192];

		private static readonly byte[] receiveScratch = new byte[8192];

		private static readonly byte[] decompressBuffer = new byte[65536];

		private static float[] sampleBuffer = new float[32768];

		private static readonly Dictionary<int, float[]> chunkPool = new Dictionary<int, float[]>();

		public static float localVoiceTime = -10f;

		private const float speakingWindow = 0.3f;

		private static readonly Dictionary<long, VoicePlayer> players = new Dictionary<long, VoicePlayer>();

		private static readonly HashSet<long> mutedPlayers = new HashSet<long>();

		private const float ReferenceDistance = 1.2f;

		private const float OcclusionInterval = 0.1f;

		private const float AcousticSmoothing = 10f;

		private static AudioListener listener;

		private static readonly Il2CppStructArray<RaycastHit> occlusionHits = new Il2CppStructArray<RaycastHit>(16L);

		public static bool IsLocalSpeaking
		{
			get
			{
				if (micEnabled)
				{
					return Time.time - localVoiceTime < 0.3f;
				}
				return false;
			}
		}

		public static bool IsSpeaking(long userId)
		{
			if (players.TryGetValue(userId, out var value))
			{
				return Time.time - value.lastReceiveTime < 0.3f;
			}
			return false;
		}

		public static bool IsMuted(long userId)
		{
			return mutedPlayers.Contains(userId);
		}

		public static void SetMuted(long userId, bool muted)
		{
			if (muted)
			{
				mutedPlayers.Add(userId);
				if (players.TryGetValue(userId, out var value) && Object.op_Implicit((Object)(object)value.source))
				{
					value.source.Stop();
					value.played = value.written;
				}
			}
			else
			{
				mutedPlayers.Remove(userId);
			}
		}

		public static void Tick()
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			bool flag = false;
			if (!SteamIntegration.hasLobby && !flag)
			{
				if (recording)
				{
					SteamUser.StopVoiceRecording();
					recording = false;
				}
				if (players.Count > 0)
				{
					Reset();
				}
				return;
			}
			if (sampleRate == 0)
			{
				sampleRate = SteamUser.GetVoiceOptimalSampleRate();
			}
			if (micEnabled && !recording)
			{
				SteamUser.StartVoiceRecording();
				recording = true;
			}
			else if (!micEnabled && recording)
			{
				SteamUser.StopVoiceRecording();
				recording = false;
			}
			uint num = default(uint);
			uint num2 = default(uint);
			if (recording && (int)SteamUser.GetAvailableVoice(ref num) == 0 && num != 0 && (int)SteamUser.GetVoice(true, compressedBuffer, (uint)compressedBuffer.Length, ref num2) == 0 && num2 != 0)
			{
				if (SteamIntegration.hasLobby)
				{
					VoiceDataMessageHandler.SendVoice(compressedBuffer, (int)num2);
				}
				localVoiceTime = Time.time;
			}
			try
			{
				UpdateAcoustics();
			}
			catch (Exception ex)
			{
				EntangleLogger.Warn("[Voice] Acoustics: " + ex.Message);
			}
			foreach (VoicePlayer value in players.Values)
			{
				if (Object.op_Implicit((Object)(object)value.source) && value.source.isPlaying)
				{
					int timeSamples = value.source.timeSamples;
					if (timeSamples < value.lastTimeSamples)
					{
						value.played += value.clipSamples - value.lastTimeSamples + timeSamples;
					}
					else
					{
						value.played += timeSamples - value.lastTimeSamples;
					}
					value.lastTimeSamples = timeSamples;
					if (value.played >= value.written)
					{
						value.source.Stop();
					}
				}
			}
		}

		public static void ReceiveVoice(long speakerId, byte[] data, int offset, int count)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			if (count <= 0 || count > receiveScratch.Length || mutedPlayers.Contains(speakerId))
			{
				return;
			}
			if (sampleRate == 0)
			{
				sampleRate = SteamUser.GetVoiceOptimalSampleRate();
			}
			Buffer.BlockCopy(data, offset, receiveScratch, 0, count);
			uint num = default(uint);
			if ((int)SteamUser.DecompressVoice(receiveScratch, (uint)count, decompressBuffer, (uint)decompressBuffer.Length, ref num, sampleRate) != 0 || num == 0)
			{
				return;
			}
			VoicePlayer player = GetPlayer(speakerId);
			if (player != null)
			{
				int num2 = (int)num / 2;
				if (sampleBuffer.Length < num2)
				{
					sampleBuffer = new float[num2];
				}
				float num3 = (float)outputVolume / 100f * 2f;
				for (int i = 0; i < num2; i++)
				{
					short num4 = (short)(decompressBuffer[i * 2] | (decompressBuffer[i * 2 + 1] << 8));
					sampleBuffer[i] = Mathf.Clamp((float)num4 / 32768f * num3, -1f, 1f);
				}
				player.lastReceiveTime = Time.time;
				WriteSamples(player, num2);
			}
		}

		private static void WriteSamples(VoicePlayer player, int count)
		{
			int num = (int)(player.written % player.clipSamples);
			int num2 = Math.Min(count, player.clipSamples - num);
			float[] chunk = GetChunk(num2);
			Array.Copy(sampleBuffer, 0, chunk, 0, num2);
			player.clip.SetData(Il2CppStructArray<float>.op_Implicit(chunk), num);
			int num3 = count - num2;
			if (num3 > 0)
			{
				float[] chunk2 = GetChunk(num3);
				Array.Copy(sampleBuffer, num2, chunk2, 0, num3);
				player.clip.SetData(Il2CppStructArray<float>.op_Implicit(chunk2), 0);
			}
			player.written += count;
			if (!player.source.isPlaying && player.written - player.played >= sampleRate / 10)
			{
				int num4 = (int)(player.played % player.clipSamples);
				player.source.timeSamples = num4;
				player.lastTimeSamples = num4;
				player.source.Play();
			}
		}

		private static float[] GetChunk(int size)
		{
			if (!chunkPool.TryGetValue(size, out var value))
			{
				value = new float[size];
				chunkPool[size] = value;
			}
			return value;
		}

		private static VoicePlayer GetPlayer(long speakerId)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: 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)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			players.TryGetValue(speakerId, out var value);
			if (value != null && Object.op_Implicit((Object)(object)value.source))
			{
				return value;
			}
			PlayerRepresentation playerRepresentation = ResolveRep(speakerId);
			if (playerRepresentation == null || (Object)(object)playerRepresentation.repRoot == (Object)null)
			{
				return null;
			}
			GameObject val = new GameObject($"Voice {speakerId}");
			Transform val2 = (Object.op_Implicit((Object)(object)playerRepresentation.repTransforms[0]) ? playerRepresentation.repTransforms[0] : playerRepresentation.repRoot);
			val.transform.SetParent(val2, false);
			val.transform.localPosition = new Vector3(0f, -0.07f, 0.09f);
			AudioSource val3 = val.AddComponent<AudioSource>();
			val3.loop = true;
			val3.playOnAwake = false;
			val3.dopplerLevel = 0f;
			val3.spread = 0f;
			val3.reverbZoneMix = 1f;
			AudioLowPassFilter val4 = val.AddComponent<AudioLowPassFilter>();
			val4.cutoffFrequency = 22000f;
			val4.lowpassResonanceQ = 1f;
			int num = (int)sampleRate;
			AudioClip clip = (val3.clip = AudioClip.Create($"VoiceClip {speakerId}", num, 1, (int)sampleRate, false));
			value = new VoicePlayer
			{
				source = val3,
				lowPass = val4,
				head = val2,
				clip = clip,
				clipSamples = num
			};
			players[speakerId] = value;
			ApplySettingsTo(value);
			return value;
		}

		private static PlayerRepresentation ResolveRep(long speakerId)
		{
			PlayerRepresentation.representations.TryGetValue(speakerId, out var value);
			return value;
		}

		public static void ApplySettings()
		{
			foreach (VoicePlayer value in players.Values)
			{
				ApplySettingsTo(value);
			}
		}

		private static void ApplySettingsTo(VoicePlayer player)
		{
			if (!Object.op_Implicit((Object)(object)player.source))
			{
				return;
			}
			player.source.volume = 1f;
			if (mode == VoiceMode.Global)
			{
				player.source.spatialBlend = 0f;
				player.source.spatialize = false;
				if (Object.op_Implicit((Object)(object)player.lowPass))
				{
					((Behaviour)player.lowPass).enabled = false;
				}
				return;
			}
			player.source.spatialBlend = 1f;
			player.source.spatialize = false;
			player.source.minDistance = 1.2f;
			player.source.maxDistance = Mathf.Max(5f, (float)proximityRange);
			player.source.rolloffMode = (AudioRolloffMode)2;
			player.source.SetCustomCurve((AudioSourceCurveType)0, BuildRolloff(player.source.maxDistance));
			if (Object.op_Implicit((Object)(object)player.lowPass))
			{
				((Behaviour)player.lowPass).enabled = true;
			}
		}

		private static AnimationCurve BuildRolloff(float maxDistance)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			AnimationCurve val = new AnimationCurve();
			for (int i = 0; i <= 24; i++)
			{
				float num = (float)i / 24f;
				float num2 = Mathf.Max(0.01f, num * maxDistance);
				float num3 = Mathf.Min(1f, 1.2f / num2);
				float num4 = Mathf.InverseLerp(1f, 0.75f, num);
				val.AddKey(num, num3 * Mathf.Clamp01(num4));
			}
			return val;
		}

		private static Transform ListenerTransform()
		{
			try
			{
				if (!Object.op_Implicit((Object)(object)listener) || !((Behaviour)listener).isActiveAndEnabled)
				{
					listener = Object.FindObjectOfType<AudioListener>();
				}
			}
			catch
			{
				listener = null;
			}
			if (Object.op_Implicit((Object)(object)listener))
			{
				return ((Component)listener).transform;
			}
			if (!Object.op_Implicit((Object)(object)Camera.main))
			{
				return null;
			}
			return ((Component)Camera.main).transform;
		}

		private static void UpdateAcoustics()
		{
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: 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_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			if (mode != VoiceMode.Proximity || players.Count == 0)
			{
				return;
			}
			Transform val = ListenerTransform();
			if (!Object.op_Implicit((Object)(object)val))
			{
				return;
			}
			float num = 1f - Mathf.Exp(-10f * Time.unscaledDeltaTime);
			foreach (VoicePlayer value in players.Values)
			{
				if (Object.op_Implicit((Object)(object)value.source) && Object.op_Implicit((Object)(object)value.lowPass))
				{
					Vector3 position = ((Component)value.source).transform.position;
					Vector3 val2 = val.position - position;
					float magnitude = ((Vector3)(ref val2)).magnitude;
					if (Time.unscaledTime >= value.nextOcclusionCheck)
					{
						value.nextOcclusionCheck = Time.unscaledTime + 0.1f;
						value.occlusion = Occlusion(position, val.position, magnitude);
					}
					float num2 = 1f;
					float num3 = 22000f;
					if (value.occlusion > 0f)
					{
						num2 *= Mathf.Pow(0.35f, Mathf.Min(value.occlusion, 3f));
						num3 = Mathf.Min(num3, Mathf.Lerp(2000f, 700f, Mathf.Clamp01(value.occlusion - 1f)));
					}
					if (Object.op_Implicit((Object)(object)value.head) && magnitude > 0.01f)
					{
						float num4 = Vector3.Dot(value.head.forward, val2 / magnitude);
						float num5 = Mathf.InverseLerp(0.3f, -1f, num4);
						num2 *= Mathf.Lerp(1f, 0.6f, num5);
						num3 = Mathf.Min(num3, Mathf.Lerp(22000f, 5000f, num5));
					}
					float num6 = Mathf.Clamp01(magnitude / Mathf.Max(5f, (float)proximityRange));
					num3 = Mathf.Min(num3, Mathf.Lerp(22000f, 7000f, num6));
					value.targetVolume = num2;
					value.targetCutoff = num3;
					value.volume = Mathf.Lerp(value.volume, value.targetVolume, num);
					value.cutoff = Mathf.Lerp(value.cutoff, value.targetCutoff, num);
					value.source.volume = value.volume;
					value.lowPass.cutoffFrequency = value.cutoff;
				}
			}
		}

		private static float Occlusion(Vector3 mouth, Vector3 ear, float distance)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			if (distance < 0.3f)
			{
				return 0f;
			}
			Vector3 val = (ear - mouth) / distance;
			int num;
			try
			{
				num = Physics.RaycastNonAlloc(mouth, val, occlusionHits, distance, -1, (QueryTriggerInteraction)1);
			}
			catch
			{
				return 0f;
			}
			float num2 = 0f;
			for (int i = 0; i < num; i++)
			{
				RaycastHit val2 = ((Il2CppArrayBase<RaycastHit>)(object)occlusionHits)[i];
				Collider collider = ((RaycastHit)(ref val2)).collider;
				if (!Object.op_Implicit((Object)(object)collider))
				{
					continue;
				}
				try
				{
					Transform root = ((Component)collider).transform.root;
					if ((!Object.op_Implicit((Object)(object)root) || (!((Object)root).name.StartsWith("PlayerRep.") && !((Object)root).name.StartsWith("Ragdoll."))) && (!Object.op_Implicit((Object)(object)PlayerScripts.playerRig) || !((Component)collider).transform.IsChildOf(((Component)PlayerScripts.playerRig).transform)))
					{
						Rigidbody attachedRigidbody = collider.attachedRigidbody;
						if ((Object)(object)attachedRigidbody == (Object)null)
						{
							num2 += 1f;
						}
						else if (Object.op_Implicit((Object)(object)((Component)attachedRigidbody).GetComponent<Joint>()))
						{
							num2 += 0.5f;
						}
					}
				}
				catch
				{
				}
			}
			return num2;
		}

		public static void Reset()
		{
			foreach (VoicePlayer value in players.Values)
			{
				if (Object.op_Implicit((Object)(object)value.source))
				{
					Object.Destroy((Object)(object)((Component)value.source).gameObject);
				}
			}
			players.Clear();
			listener = null;
		}
	}
}
namespace Entanglement.UI
{
	public static class LoadingScreen
	{
		public static AssetBundle assetBundle;

		public static void LoadBundle()
		{
			assetBundle = EmebeddedAssetBundle.LoadFromAssembly(EntanglementMod.entanglementAssembly, "Entanglement.resources.logo.eres");
		}

		public static void OverrideScreen()
		{
			if ((Object)(object)assetBundle == (Object)null)
			{
				return;
			}
			try
			{
				Texture2D val = assetBundle.LoadAsset<Texture2D>("entanglement.png");
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				GameObject val2 = GameObject.Find("Canvas/RawImage (1)");
				if (!((Object)(object)val2 == (Object)null))
				{
					RawImage component = val2.GetComponent<RawImage>();
					if (!((Object)(object)component == (Object)null))
					{
						component.texture = (Texture)(object)val;
					}
				}
			}
			catch
			{
			}
		}
	}
	public static class DownloadUI
	{
		private const float Smooth = 0.35f;

		private const float Tau = (float)Math.PI * 2f;

		private const float HalfPi = (float)Math.PI / 2f;

		private const int Wedges = 16;

		private const float Inner = 0.016f;

		private const float Outer = 0.078f;

		private const float Gap = 0.045f;

		private static readonly Vector3 Offset = new Vector3(0.02f, 0.07f, 0.04f);

		private static GameObject root;

		private static MeshRenderer ringRenderer;

		private static Mesh ringMesh;

		private static Color32[] colors;

		private static TextMeshProUGUI label;

		private static bool buildFailed;

		private static bool snapped;

		private static Texture2D whiteTex;

		public static void OnSceneWasInitialized()
		{
			label = null;
			ringRenderer = null;
			ringMesh = null;
			colors = null;
			root = null;
			snapped = false;
			buildFailed = false;
		}

		public static void Update()
		{
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			bool flag = FileTransferManager.HasPendingConsent || Thunderstore.HasPending;
			bool flag2 = FileTransferManager.HasActiveDownloads || Thunderstore.IsBusy;
			if (!flag && !flag2)
			{
				if ((Object)(object)root != (Object)null && root.activeSelf)
				{
					root.SetActive(false);
				}
				snapped = false;
			}
			else
			{
				if (!EnsureBuilt())
				{
					return;
				}
				if (!root.activeSelf)
				{
					root.SetActive(true);
				}
				FollowWrist();
				float num;
				Color color = default(Color);
				string text;
				string name;
				float doneMb;
				float totalMb;
				float progress;
				if (flag)
				{
					FileTransfer fileTransfer = FileTransferManager.LargestPendingConsent();
					num = ((fileTransfer != null && fileTransfer.totalBytes > 0) ? Mathf.Clamp01((float)fileTransfer.receivedBytes / (float)fileTransfer.totalBytes) : 0.15f);
					((Color)(ref color))..ctor(1f, 0.78f, 0.15f, 1f);
					if (fileTransfer != null)
					{
						text = SizeLine(Path.GetFileNameWithoutExtension(fileTransfer.fileName), (float)fileTransfer.receivedBytes / 1024f / 1024f, (float)fileTransfer.totalBytes / 1024f / 1024f);
					}
					else
					{
						List<Thunderstore.PendingOffer> pending = Thunderstore.GetPending();
						text = ((pending.Count > 0) ? ("Download " + pending[0].label + "\nCircle, Downloads") : "Permission needed\nCircle, Downloads");
					}
				}
				else if (Thunderstore.TryGetProgress(out name, out doneMb, out totalMb, out progress))
				{
					num = progress;
					((Color)(ref color))..ctor(0.35f, 0.9f, 1f, 1f);
					text = ((totalMb > 0f) ? SizeLine(name, doneMb, totalMb) : SizeLine(name, 0f, 0f));
				}
				else
				{
					num = FileTransferManager.TotalDownloadProgress();
					((Color)(ref color))..ctor(0.35f, 0.9f, 1f, 1f);
					FileTransfer fileTransfer2 = FileTransferManager.LargestActiveDownload();
					text = ((fileTransfer2 != null) ? SizeLine(Path.GetFileNameWithoutExtension(fileTransfer2.fileName), (float)fileTransfer2.receivedBytes / 1024f / 1024f, (float)fileTransfer2.totalBytes / 1024f / 1024f) : SizeLine("Downloading", num, 1f));
				}
				Paint(num, color);
				if ((Object)(object)label != (Object)null)
				{
					((TMP_Text)label).text = text;
				}
			}
		}

		private static bool EnsureBuilt()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			if ((Object)(object)root != (Object)null)
			{
				return true;
			}
			if (buildFailed)
			{
				return false;
			}
			if ((Object)(object)Wrist() == (Object)null || !Object.op_Implicit((Object)(object)PlayerScripts.playerRig))
			{
				return false;
			}
			try
			{
				root = new GameObject("EntanglementDownloadPie");
				BuildRing();
				BuildLabel();
				snapped = false;
				return true;
			}
			catch (Exception ex)
			{
				EntangleLogger.Warn("[DownloadUI] Failed to build the pie meter: " + ex.Message);
				buildFailed = true;
				if ((Object)(object)root != (Object)null)
				{
					Object.Destroy((Object)(object)root);
				}
				root = null;
				return false;
			}
		}

		private static void BuildRing()
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: 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)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Expected O, but got Unknown
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			Vector3[] array = (Vector3[])(object)new Vector3[64];
			int[] array2 = new int[96];
			colors = (Color32[])(object)new Color32[64];
			for (int i = 0; i < 16; i++)
			{
				float angle = (float)i / 16f * ((float)Math.PI * 2f) - (float)Math.PI / 2f + 0.0225f;
				float angle2 = (float)(i + 1) / 16f * ((float)Math.PI * 2f) - (float)Math.PI / 2f - 0.0225f;
				int num = i * 4;
				array[num] = Polar(angle, 0.016f);
				array[num + 1] = Polar(angle, 0.078f);
				array[num + 2] = Polar(angle2, 0.078f);
				array[num + 3] = Polar(angle2, 0.016f);
				int num2 = i * 6;
				array2[num2] = num;
				array2[num2 + 1] = num + 1;
				array2[num2 + 2] = num + 2;
				array2[num2 + 3] = num;
				array2[num2 + 4] = num + 2;
				array2[num2 + 5] = num + 3;
			}
			ringMesh = new Mesh();
			ringMesh.vertices = Il2CppStructArray<Vector3>.op_Implicit(array);
			ringMesh.triangles = Il2CppStructArray<int>.op_Implicit(array2);
			ringMesh.colors32 = Il2CppStructArray<Color32>.op_Implicit(colors);
			ringMesh.RecalculateBounds();
			GameObject val = new GameObject("Ring");
			val.transform.SetParent(root.transform, false);
			val.AddComponent<MeshFilter>().sharedMesh = ringMesh;
			ringRenderer = val.AddComponent<MeshRenderer>();
			((Renderer)ringRenderer).sharedMaterial = RingMaterial();
			((Renderer)ringRenderer).shadowCastingMode = (ShadowCastingMode)0;
			((Renderer)ringRenderer).receiveShadows = false;
		}

		private static string SizeLine(string name, float doneMb, float totalMb)
		{
			if (string.IsNullOrEmpty(name))
			{
				name = "Download";
			}
			if (name.Length > 18)
			{
				name = name.Substring(0, 17) + "...";
			}
			return $"{name}\n{doneMb:0.0}/{totalMb:0.0} MB";
		}

		private static Vector3 Polar(float angle, float radius)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			return new Vector3(Mathf.Cos(angle) * radius, Mathf.Sin(angle) * radius, 0f);
		}

		private static Material RingMaterial()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			Shader val = Shader.Find("Sprites/Default");
			if ((Object)(object)val == (Object)null)
			{
				val = Shader.Find("UI/Default");
			}
			if ((Object)(object)val == (Object)null)
			{
				val = Shader.Find("Unlit/Transparent");
			}
			Material val2 = new Material(val);
			val2.mainTexture = (Texture)(object)WhiteTex();
			val2.color = Color.white;
			if (val2.HasProperty("_Cull"))
			{
				val2.SetInt("_Cull", 0);
			}
			val2.renderQueue = 4000;
			return val2;
		}

		private static Texture2D WhiteTex()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)whiteTex != (Object)null)
			{
				return whiteTex;
			}
			whiteTex = new Texture2D(1, 1, (TextureFormat)4, false);
			whiteTex.SetPixel(0, 0, Color.white);
			whiteTex.Apply();
			return whiteTex;
		}

		private static void BuildLabel()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: 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_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: 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)
			GameObject val = new GameObject("Label");
			val.transform.SetParent(root.transform, false);
			val.transform.localPosition = new Vector3(0f, 0.012f, -0.02f);
			val.transform.localRotation = Quaternion.Euler(0f, 180f, 0f);
			val.transform.localScale = new Vector3(-0.0008f, 0.0008f, 0.0008f);
			Canvas obj = val.AddComponent<Canvas>();
			obj.renderMode = (RenderMode)2;
			obj.sortingOrder = 500;
			label = val.AddComponent<TextMeshProUGUI>();
			if ((Object)(object)((TMP_Text)label).font == (Object)null && Object.op_Implicit((Object)(object)PlayerScripts.playerRig))
			{
				TextMeshProUGUI componentInChildren = ((Component)PlayerScripts.playerRig).GetComponentInChildren<TextMeshProUGUI>(true);
				if ((Object)(object)componentInChildren != (Object)null && (Object)(object)((TMP_Text)componentInChildren).font != (Object)null)
				{
					((TMP_Text)label).font = ((TMP_Text)componentInChildren).font;
				}
			}
			((TMP_Text)label).alignment = (TextAlignmentOptions)514;
			((TMP_Text)label).enableWordWrapping = false;
			((TMP_Text)label).overflowMode = (TextOverflowModes)0;
			((TMP_Text)label).color = Color.white;
			((TMP_Text)label).fontSize = 48f;
			((TMP_Text)label).rectTransform.sizeDelta = new Vector2(520f, 180f);
			((TMP_Text)label).text = "test download\n0.0/10.0 MB";
		}

		private static void FollowWrist()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: 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_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: 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_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: 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_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: 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_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: 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_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: 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)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			Hand val = Wrist();
			Transform val2 = Head();
			if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null) && !((Object)(object)val.palmPositionTransform == (Object)null))
			{
				Vector3 position = val.palmPositionTransform.position;
				Vector3 val3 = val2.position - position;
				val3.y = 0f;
				if (((Vector3)(ref val3)).sqrMagnitude < 0.0001f)
				{
					val3 = -val2.forward;
				}
				((Vector3)(ref val3)).Normalize();
				Vector3 val4 = position + Vector3.up * Offset.y + ((Component)val).transform.right * Offset.x + val3 * Offset.z;
				Quaternion val5 = Quaternion.LookRotation(val4 - val2.position, Vector3.up);
				if (!snapped)
				{
					root.transform.SetPositionAndRotation(val4, val5);
					snapped = true;
				}
				else
				{
					root.transform.position = Vector3.Lerp(root.transform.position, val4, 0.35f);
					root.transform.rotation = Quaternion.Slerp(root.transform.rotation, val5, 0.35f);
				}
			}
		}

		private static void Paint(float progress, Color color)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: 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_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: 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_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)ringMesh == (Object)null) && colors != null)
			{
				float num = Mathf.Clamp01(progress) * 16f;
				Color32 val = default(Color32);
				((Color32)(ref val))..ctor((byte)28, (byte)30, (byte)36, (byte)230);
				Color32 val2 = Color32.op_Implicit(color);
				for (int i = 0; i < 16; i++)
				{
					float num2 = Mathf.Clamp01(num - (float)i);
					Color32 val3 = Color32.Lerp(val, val2, num2);
					int num3 = i * 4;
					colors[num3] = val3;
					colors[num3 + 1] = val3;
					colors[num3 + 2] = val3;
					colors[num3 + 3] = val3;
				}
				ringMesh.colors32 = Il2CppStructArray<Color32>.op_Implicit(colors);
			}
		}

		private static Hand Wrist()
		{
			Hand playerLeftHand = PlayerScripts.playerLeftHand;
			if ((Object)(object)playerLeftHand != (Object)null && (Object)(object)playerLeftHand.palmPositionTransform != (Object)null)
			{
				return playerLeftHand;
			}
			Hand playerRightHand = PlayerScripts.playerRightHand;
			if ((Object)(object)playerRightHand != (Object)null && (Object)(object)playerRightHand.palmPositionTransform != (Object)null)
			{
				return playerRightHand;
			}
			return null;
		}

		private static Transform Head()
		{
			if (!Object.op_Implicit((Object)(object)PlayerScripts.playerRig))
			{
				return null;
			}
			Transform val = ((Component)PlayerScripts.playerRig).transform.Find("[PhysicsRig]/Head");
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			if (!((Object)(object)Camera.main != (Object)null))
			{
				return null;
			}
			return ((Component)Camera.main).transform;
		}
	}
	public static class EntanglementRadial
	{
		private const string ButtonName = "Entanglement Redux";

		private static bool added;

		private static int framesUntilTry = int.MaxValue;

		private static readonly string[] spareRegions = new string[6] { "button_Region_S", "button_Region_SW", "button_Region_W", "button_Region_NE", "button_Region_E", "button_Region_SE" };

		public static void OnSceneWasInitialized()
		{
			added = false;
			framesUntilTry = 0;
		}

		public static void EnsureVisible()
		{
			framesUntilTry = 0;
			TryAdd(refreshPage: true);
		}

		public static void Tick()
		{
			if (!added)
			{
				if (framesUntilTry > 0)
				{
					framesUntilTry--;
				}
				else
				{
					TryAdd();
				}
			}
		}

		private static void TryAdd(bool refreshPage = false)
		{
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Expected O, but got Unknown
			try
			{
				GameObject val = null;
				if (Object.op_Implicit((Object)(object)PlayerScripts.playerRig))
				{
					val = ((Component)PlayerScripts.playerRig).gameObject;
				}
				if (!Object.op_Implicit((Object)(object)val))
				{
					val = Player.GetRigManager();
				}
				if (!Object.op_Implicit((Object)(object)val))
				{
					return;
				}
				Transform val2 = val.transform.Find("[UIRig]/PLAYERUI/panel_Default");
				if (!Object.op_Implicit((Object)(object)val2))
				{
					return;
				}
				PageView component = ((Component)val2).GetComponent<PageView>();
				if ((Object)(object)component == (Object)null || component.m_HomePage == null || component.m_HomePage.items == null)
				{
					return;
				}
				Object.FindObjectOfType<PopUpMenuView>();
				RemoveExtraEntanglement(component);
				if (HasEntanglement(component))
				{
					added = true;
					if (refreshPage)
					{
						component.Render(component.m_HomePage);
					}
					return;
				}
				string[] array = spareRegions;
				foreach (string text in array)
				{
					Directions val3 = DirectionFromRegion(text);
					if (DirectionTaken(component, val3))
					{
						continue;
					}
					Transform val4 = val2.Find(text);
					if (!Object.op_Implicit((Object)(object)val4))
					{
						continue;
					}
					PageItemView component2 = ((Component)val4).GetComponent<PageItemView>();
					if (!Object.op_Implicit((Object)(object)component2))
					{
						continue;
					}
					Action action = delegate
					{
						if (!GameRadial.Open())
						{
							EntangleLogger.Warn("[EntanglementRadial] Could not open the Entanglement Redux menu.");
						}
					};
					component2.m_Data = new PageItem("Entanglement Redux", val3, Action.op_Implicit(action));
					component.m_HomePage.items.Add(component2.m_Data);
					added = true;
					if (refreshPage || !component.m_IsActivated)
					{
						component.Render(component.m_HomePage);
					}
					EntangleLogger.Log("[EntanglementRadial] Added Entanglement on " + text);
					return;
				}
				EntangleLogger.Warn("[EntanglementRadial] No free circle-menu slot.");
				added = true;
			}
			catch (Exception ex)
			{
				EntangleLogger.Warn("[EntanglementRadial] Failed to add circle button: " + ex.Message);
				added = true;
			}
		}

		private static bool HasEntanglement(PageView pageView)
		{
			for (int i = 0; i < pageView.m_HomePage.items.Count; i++)
			{
				PageItem val = pageView.m_HomePage.items[i];
				if (val != null && val.name == "Entanglement Redux")
				{
					return true;
				}
			}
			return false;
		}

		private static void RemoveExtraEntanglement(PageView pageView)
		{
			bool flag = false;
			List<PageItem> items = pageView.m_HomePage.items;
			for (int num = items.Count - 1; num >= 0; num--)
			{
				PageItem val = items[num];
				if (val != null && !(val.name != "Entanglement Redux"))
				{
					if (!flag)
					{
						flag = true;
					}
					else
					{
						items.RemoveAt(num);
					}
				}
			}
		}

		private static bool DirectionTaken(PageView pageView, Directions direction)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < pageView.m_HomePage.items.Count; i++)
			{
				PageItem val = pageView.m_HomePage.items[i];
				if (val != null && val.direction == direction)
				{
					return true;
				}
			}
			return false;
		}

		private static Directions DirectionFromRegion(string region)
		{
			if (!region.EndsWith("_NE"))
			{
				if (!region.EndsWith("_NW"))
				{
					if (!region.EndsWith("_SE"))
					{
						if (!region.EndsWith("_SW"))
						{
							if (!region.EndsWith("_N"))
							{
								if (!region.EndsWith("_S"))
								{
									if (!region.EndsWith("_E"))
									{
										return (Directions)6;
									}
									return (Directions)2;
								}
								return (Directions)4;
							}
							return (Directions)0;
						}
						return (Directions)5;
					}
					return (Directions)3;
				}
				return (Directions)7;
			}
			return (Directions)1;
		}
	}
	[HarmonyPatch(typeof(PopUpMenuView), "Activate")]
	public static class EntanglementRadialOpenPatch
	{
		public static void Prefix()
		{
			EntanglementRadial.EnsureVisible();
		}

		public static void Postfix()
		{
			try
			{
				GameRadial.OnMenuOpened();
			}
			catch (Exception e)
			{
				PatchGuard.Report("EntanglementRadial.OpenPostfix", e);
			}
		}
	}
	public static class GameNotify
	{
		private struct Pending
		{
			public string text;

			public float seconds;
		}

		private static UI_Subtitle_Control subtitles;

		private static int framesUntilReady;

		private static bool ready;

		private static readonly List<Pending> queued = new List<Pending>();

		public static void OnSceneWasInitialized()
		{
			subtitles = null;
			ready = false;
			framesUntilReady = 90;
		}

		public static void Tick()
		{
			if (ready || framesUntilReady <= 0)
			{
				return;
			}
			framesUntilReady--;
			if (framesUntilReady <= 0)
			{
				ready = true;
				for (int i = 0; i < queued.Count; i++)
				{
					Deliver(queued[i].text, queued[i].seconds);
				}
				queued.Clear();
			}
		}

		public static void Show(string text, float seconds = 4f)
		{
			if (!string.IsNullOrEmpty(text))
			{
				if (!ready)
				{
					queued.Add(new Pending
					{
						text = text,
						seconds = seconds
					});
				}
				else
				{
					Deliver(text, seconds);
				}
			}
		}

		private static void Deliver(string text, float seconds)
		{
			string text2 = text.Replace("\r", " ").Replace("\n", " ").Trim();
			if (text2.Length > 90)
			{
				text2 = text2.Substring(0, 87) + "...";
			}
			try
			{
				if ((Object)(object)subtitles == (Object)null)
				{
					subtitles = Object.FindObjectOfType<UI_Subtitle_Control>();
				}
				if ((Object)(object)subtitles != (Object)null)
				{
					subtitles.AddSubtitles(text2, Mathf.Max(2f, seconds), (SubtitlePlacement)1);
					return;
				}
			}
			catch (Exception ex)
			{
				subtitles = null;
				EntangleLogger.Warn("[GameNotify] Subtitle banner unavailable: " + ex.Message);
			}
			try
			{
				Notifications.SendNotification(text, seconds);
			}
			catch
			{
			}
		}
	}
	public static class GameRadial
	{
		private struct Entry
		{
			public string label;

			public Action action;

			public bool wide;

			public Entry(string label, Action action, bool wide = false)
			{
				this.label = label;
				this.action = action;
				this.wide = wide;
			}
		}

		private static Directions[] slots;

		private static PageView pageView;

		private static Page pending;

		private static bool animateNext = true;

		private static float nextClick;

		private static CallResult<LobbyMatchList_t> lobbySearch;

		private static int searchGen;

		private static int searchWaiting;

		private static bool viewingServers;

		private static readonly List<CSteamID> servers = new List<CSteamID>();

		private static int playerPage;

		private static int downloadPage;

		private static int serverPage;

		private static bool downloadsWanted;

		private static int bannedPage;

		private static Directions[] Slots()
		{
			if (slots != null)
			{
				return slots;
			}
			Directions[] array = new Directions[7];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			slots = (Directions[])(object)array;
			return slots;
		}

		public static bool AllowClick()
		{
			if (Time.unscaledTime < nextClick)
			{
				return false;
			}
			nextClick = Time.unscaledTime + 0.35f;
			return true;
		}

		private static Action Once(Action action)
		{
			return delegate
			{
				if (AllowClick())
				{
					action?.Invoke();
				}
			};
		}

		public static void OnSceneWasInitialized()
		{
			pageView = null;
			pending = null;
		}

		public static void Tick()
		{
			if (pending != null && Bind())
			{
				if (animateNext)
				{
					pageView.ChangePage(pending);
				}
				else
				{
					pageView.Render(pending);
				}
				pending = null;
				animateNext = true;
			}
		}

		public static bool Open()
		{
			if (!AllowClick())
			{
				return true;
			}
			if (!Bind())
			{
				return false;
			}
			ShowHub();
			return true;
		}

		private static bool Bind()
		{
			if (Object.op_Implicit((Object)(object)pageView))
			{
				return true;
			}
			GameObject val = null;
			if (Object.op_Implicit((Object)(object)PlayerScripts.playerRig))
			{
				val = ((Component)PlayerScripts.playerRig).gameObject;
			}
			if (!Object.op_Implicit((Object)(object)val))
			{
				val = Player.GetRigManager();
			}
			if (!Object.op_Implicit((Object)(object)val))
			{
				return false;
			}
			Transform val2 = val.transform.Find("[UIRig]/PLAYERUI/panel_Default");
			if (!Object.op_Implicit((Object)(object)val2))
			{
				return false;
			}
			pageView = ((Component)val2).GetComponent<PageView>();
			if ((Object)(object)pageView != (Object)null)
			{
				return pageView.m_HomePage != null;
			}
			return false;
		}

		private static void Show(Page page, bool animate = true)
		{
			pending = page;
			animateNext = animate;
		}

		private static void ShowHome()
		{
			viewingServers = false;
			if ((Object)(object)pageView != (Object)null && pageView.m_HomePage != null)
			{
				Show(pageView.m_HomePage);
			}
		}

		private static Page PageOf(params Entry[] entries)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Expected O, but got Unknown
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Expected O, but got Unknown
			Page val = new Page();
			val.items = new List<PageItem>();
			Directions[] array = Slots();
			int num = Math.Min(entries.Length, array.Length);
			for (int i = 0; i < num; i++)
			{
				val.items.Add(new PageItem(Fit(entries[i].label, entries[i].wide), array[i], Action.op_Implicit(Once(entries[i].action))));
			}
			val.items.Add(new PageItem("Back", (Directions)4, Action.op_Implicit(Once(ShowHome))));
			return val;
		}

		private static Page PageBack(Action back, params Entry[] entries)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Expected O, but got Unknown
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Expected O, but got Unknown
			Page val = new Page();
			val.items = new List<PageItem>();
			Directions[] array = Slots();
			int num = Math.Min(entries.Length, array.Length);
			for (int i = 0; i < num; i++)
			{
				val.items.Add(new PageItem(Fit(entries[i].label, entries[i].wide), array[i], Action.op_Implicit(Once(entries[i].action))));
			}
			val.items.Add(new PageItem("Back", (Directions)4, Action.op_Implicit(Once(back))));
			return val;
		}

		private static void ShowHub()
		{
			viewingServers = false;
			Show(PageOf(new Entry("Lobby", ShowLobby), new Entry("Server Browser", ShowServers), new Entry("Players", ShowPlayers), new Entry("Downloads", ShowDownloads), new Entry("Voice", ShowVoice), new Entry(VoiceManager.micEnabled ? "Mic On" : "Mic Off", delegate
			{
				VoiceManager.micEnabled = !VoiceManager.micEnabled;
				ShowHub();
			}), new Entry("Settings", ShowSettings)));
		}

		private static void ShowLobby()
		{
			Show(PageBack(ShowHub, new Entry("Start Server", delegate
			{
				Server.StartServer();
				ShowLobby();
			}), new Entry("Suicide", delegate
			{
				if (!SteamIntegration.hasLobby)
				{
					GameNotify.Show("You need to be in a server to do that.", 3f);
				}
				else
				{
					PlayerDeathManager.Suicide();
				}
			}), new Entry("Stop Server", delegate
			{
				if (Server.instance == null && (!SteamIntegration.hasLobby || !SteamIntegration.isHost))
				{
					GameNotify.Show(SteamIntegration.hasLobby ? "You're not the host. Use Disconnect." : "No server running.", 3f);
					ShowLobby();
				}
				else
				{
					if (Server.StopHosting())
					{
						GameNotify.Show("Server stopped.", 3f);
					}
					else
					{
						GameNotify.Show("Couldn't stop the server.", 3f);
					}
					ShowLobby();
				}
			}), new Entry("Disconnect", delegate
			{
				if (Node.isServer)
				{
					GameNotify.Show("You're the host. Use Stop Server.", 3f);
					ShowLobby();
				}
				else
				{
					if (Node.activeNode is Client client)
					{
						client.DisconnectFromServer();
					}
					else if (!SteamIntegration.hasLobby)
					{
						GameNotify.Show("You're not in a server.", 3f);
					}
					ShowLobby();
				}
			}), new Entry("Invite", InviteSteam), new Entry("Lobby Options", ShowLobbyOptions)), animate: false);
		}

		private static void InviteSteam()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			if (!SteamIntegration.hasLobby)
			{
				GameNotify.Show("Start or join a server first.", 3f);
				return;
			}
			SteamIntegration.UpdateActivity();
			DiscordInvite.Refresh();
			ShowHome();
			SteamFriends.ActivateGameOverlayInviteDialog(SteamIntegration.lobby);
		}

		private static void ShowLobbyOptions()
		{
			Show(PageBack(ShowLobby, new Entry(PlayerSlotLabel(), delegate
			{
				int num = Server.maxPlayers + 1;
				if (num > 16)
				{
					num = 1;
				}
				Server.maxPlayers = (byte)num;
				Server.instance?.UpdateLobbyConfig();
				ShowLobbyOptions();
			}), new Entry(Server.isLocked ? "Locked" : "Open", delegate
			{
				Server.isLocked = !Server.isLocked;
				Server.instance?.UpdateLobbyConfig();
				ShowLobbyOptions();
			}), new Entry(Server.visibility.ToString(), delegate
			{
				Server.visibility = (ServerVisibility)((int)(Server.visibility + 1) % 3);
				Server.instance?.UpdateLobbyConfig();
				ShowLobbyOptions();
			}), new Entry(SlowMoSync.Label(), delegate
			{
				if (SteamIntegration.hasLobby && !SteamIntegration.isHost)
				{
					GameNotify.Show("Only the host can change slow-mo sync.", 3f);
					ShowLobbyOptions();
				}
				else
				{
					SlowMoSync.Cycle();
					ShowLobbyOptions();
				}
			}), new Entry(Server.ClientTeleportAllowed ? "Teleport: Everyone" : "Teleport: Host Only", delegate
			{
				if (SteamIntegration.hasLobby && !SteamIntegration.isHost)
				{
					GameNotify.Show("Only the host can change who can teleport.", 3f);
					ShowLobbyOptions();
				}
				else
				{
					Server.clientTeleport = !Server.clientTeleport;
					Server.instance?.UpdateLobbyConfig();
					ShowLobbyOptions();
				}
			})), animate: false);
		}

		private static void ShowServers()
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			viewingServers = true;
			serverPage = 0;
			servers.Clear();
			searchGen++;
			searchWaiting = searchGen;
			if (lobbySearch == null)
			{
				lobbySearch = CallResult<LobbyMatchList_t>.Create((APIDispatchDelegate<LobbyMatchList_t>)OnLobbySearch);
			}
			SteamMatchmaking.AddRequestLobbyListStringFilter("entanglement", "true", (ELobbyComparison)0);
			SteamMatchmaking.AddRequestLobbyListDistanceFilter((ELobbyDistanceFilter)3);
			SteamMatchmaking.AddRequestLobbyListResultCountFilter(50);
			lobbySearch.Set(SteamMatchmaking.RequestLobbyList(), (APIDispatchDelegate<LobbyMatchList_t>)null);
			ShowServerPage(searching: true);
		}

		private static void OnLobbySearch(LobbyMatchList_t result, bool failed)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			if (searchWaiting != searchGen)
			{
				return;
			}
			servers.Clear();
			if (!failed)
			{
				int nLobbiesMatching = (int)result.m_nLobbiesMatching;
				for (int i = 0; i < nLobbiesMatching; i++)
				{
					servers.Add(SteamMatchmaking.GetLobbyByIndex(i));
				}
			}
			IncludeLocalLobby();
			if (viewingServers)
			{
				ShowServerPage(searching: false);
			}
		}

		private static void IncludeLocalLobby()
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: 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)
			if (!SteamIntegration.hasLobby)
			{
				return;
			}
			CSteamID lobby = SteamIntegration.lobby;
			for (int i = 0; i < servers.Count; i++)
			{
				if (servers[i] == lobby)
				{
					return;
				}
			}
			servers.Insert(0, lobby);
		}

		private static void ShowServerPage(bool searching)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Expected O, but got Unknown
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Expected O, but got Unknown
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Expected O, but got Unknown
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Expected O, but got Unknown
			//IL_0233: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Expected O, but got Unknown
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Expected O, but got Unknown
			if (!viewingServers)
			{
				return;
			}
			int num = ((servers.Count == 0) ? 1 : ((servers.Count + 5 - 1) / 5));
			if (serverPage >= num)
			{
				serverPage = 0;
			}
			if (serverPage < 0)
			{
				serverPage = 0;
			}
			Page val = new Page();
			val.items = new List<PageItem>();
			val.items.Add(new PageItem(PageArrow(-1, num), (Directions)5, Action.op_Implicit(Once(delegate
			{
				StepServerPage(-1);
			}))));
			val.items.Add(new PageItem(PageArrow(1, num), (Directions)3, Action.op_Implicit(Once(delegate
			{
				StepServerPage(1);
			}))));
			Directions[] array = new Directions[5];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			Directions[] array2 = (Directions[])(object)array;
			if (searching && servers.Count == 0)
			{
				val.items.Add(new PageItem("Searching...", array2[0], Action.op_Implicit(Once(delegate
				{
				}))));
			}
			else if (servers.Count == 0)
			{
				val.items.Add(new PageItem("No servers", array2[0], Action.op_Implicit(Once(delegate
				{
				}))));
			}
			else
			{
				int num2 = serverPage * 5;
				int num3 = 0;
				for (int num4 = num2; num4 < servers.Count; num4++)
				{
					if (num3 >= array2.Length)
					{
						break;
					}
					CSteamID id = servers[num4];
					val.items.Add(new PageItem(Fit(ServerLabel(id)), array2[num3], Action.op_Implicit(Once(delegate
					{
						//IL_0001: Unknown result type (might be due to invalid IL or missing references)
						Join(id);
					}))));
					num3++;
				}
			}
			val.items.Add(new PageItem("Back", (Directions)4, Action.op_Implicit(Once(ShowHub))));
			Show(val, animate: false);
		}

		private static string PageArrow(int dir, int pages)
		{
			if (pages <= 1)
			{
				if (dir >= 0)
				{
					return ">";
				}
				return "<";
			}
			if (dir < 0)
			{
				return "< " + (serverPage + 1);
			}
			return serverPage + 1 + "/" + pages + " >";
		}

		private static int ServerPageCount()
		{
			if (servers.Count == 0)
			{
				return 1;
			}
			return (servers.Count + 5 - 1) / 5;
		}

		private static void StepServerPage(int dir)
		{
			int num = ServerPageCount();
			if (num <= 1)
			{
				GameNotify.Show("No more pages.", 2f);
				return;
			}
			serverPage = (serverPage + dir + num) % num;
			ShowServerPage(searching: false);
		}

		private static void Join(CSteamID lobbyId)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			viewingServers = false;
			ShowHome();
			if (lobbyId == CSteamID.Nil)
			{
				GameNotify.Show("That Server no longer exists.", 3f);
			}
			else if (SteamIntegration.hasLobby)
			{
				if (SteamIntegration.lobby == lobbyId)
				{
					GameNotify.Show("You're already in this server.", 3f);
				}
				else
				{
					GameNotify.Show("Leave your server before joining another.", 3f);
				}
			}
			else
			{
				string lobbyData = SteamMatchmaking.GetLobbyData(lobbyId, "host_name");
				GameNotify.Show(string.IsNullOrEmpty(lobbyData) ? "Joining..." : ("Joining " + lobbyData + "..."), 3f);
				Client.instance?.JoinLobby(lobbyId);
			}
		}

		private static string ServerLabel(CSteamID lobbyId)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: 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)
			string text = SteamMatchmaking.GetLobbyData(lobbyId, "host_name");
			if (string.IsNullOrEmpty(text))
			{
				text = "Unknown";
			}
			SteamIntegration.LobbyOccupancy(lobbyId, out var current, out var limit, out var _);
			if (SteamMatchmaking.GetLobbyData(lobbyId, "locked") == "1")
			{
				return text + " locked";
			}
			if (limit <= 0)
			{
				return text;
			}
			return text + " " + current + "/" + limit;
		}

		private static string PlayerSlotLabel()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			if (!SteamIntegration.hasLobby)
			{
				return $"Players {Server.maxPlayers}";
			}
			SteamIntegration.LobbyOccupancy(SteamIntegration.lobby, out var _, out var limit, out var open);
			if (SteamIntegration.isHost && Server.isLocked)
			{
				return $"Players {limit} (Locked)";
			}
			if (open <= 0)
			{
				return $"Players {limit} (Full)";
			}
			return $"Players {limit} ({open} open)";
		}

		private static void ShowPlayers()
		{
			playerPage = 0;
			ShowPlayerPage();
		}

		private static void ShowPlayerPage()
		{
			ShowWheelPage(Members().ConvertAll(delegate(long id)
			{
				string name = SteamIntegration.GetUserName(id);
				return new Entry(name, delegate
				{
					ShowPlayer(id, name);
				});
			}), playerPage, delegate(int page)
			{
				playerPage = page;
				ShowPlayerPage();
			}, ShowHub, "Just you");
		}

		private static void ShowPlayer(long userId, string name)
		{
			List<Entry> list = new List<Entry>
			{
				new Entry("Steam Profile", delegate
				{
					//IL_000b: Unknown result type (might be due to invalid IL or missing references)
					SteamFriends.ActivateGameOverlayToUser("steamid", new CSteamID((ulong)userId));
				})
			};
			if (SteamIntegration.isHost)
			{
				list.Add(new Entry("Kick", delegate
				{
					Server.instance?.KickUser(userId, name);
					ShowPlayers();
				}));
				list.Add(new Entry("Ban", delegate
				{
					BanList.BanUser(userId, name);
					Server.instance?.KickUser(userId, name, DisconnectReason.Banned);
					ShowPlayers();
				}));
			}
			if (SteamIntegration.isHost || Server.ClientTeleportAllowed)
			{
				list.Add(new Entry("Teleport To", delegate
				{
					string text = Server.TeleportTo(userId);
					if (text != null)
					{
						GameNotify.Show(text, 3f);
					}
				}));
			}
			bool muted = VoiceManager.IsMuted(userId);
			list.Add(new Entry(muted ? "Unmute" : "Mute", delegate
			{
				VoiceManager.SetMuted(userId, !muted);
				ShowPlayer(userId, name);
			}));
			Show(PageBack(ShowPlayers, list.ToArray()), animate: false);
		}

		private static List<long> Members()
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			List<long> list = new List<long>();
			if (!SteamIntegration.hasLobby)
			{
				return list;
			}
			int numLobbyMembers = SteamMatchmaking.GetNumLobbyMembers(SteamIntegration.lobby);
			for (int i = 0; i < numLobbyMembers; i++)
			{
				long steamID = (long)SteamMatchmaking.GetLobbyMemberByIndex(SteamIntegration.lobby, i).m_SteamID;
				if (steamID != SteamIntegration.currentUserId)
				{
					list.Add(steamID);
				}
			}
			return list;
		}

		public static void ForceOpenDownloads()
		{
			downloadPage = 0;
			downloadsWanted = true;
			if (Bind())
			{
				ShowDownloadPage();
			}
		}

		public static void OnMenuOpened()
		{
			if (downloadsWanted)
			{
				if (!FileTransferManager.HasPendingConsent && !Thunderstore.HasPending)
				{
					downloadsWanted = false;
				}
				else if (Bind())
				{
					downloadsWanted = false;
					downloadPage = 0;
					ShowDownloadPage();
				}
			}
		}

		private static void ShowDownloads()
		{
			downloadPage = 0;
			ShowDownloadPage();
		}

		private static void ShowDownloadPage()
		{
			List<FileTransfer> pendingConsentTransfers = FileTransferManager.GetPendingConsentTransfers();
			List<Thunderstore.PendingOffer> list = Thunderstore.GetPending();
			List<Entry> list2 = new List<Entry>();
			if (pendingConsentTransfers.Count > 0 || list.Count > 0)
			{
				list2.Add(new Entry("Accept All", delegate
				{
					FileTransferManager.AcceptAllPending();
					Thunderstore.AcceptAllPending();
					GameNotify.Show("Accepted pending downloads.", 3f);
					ShowDownloads();
				}));
			}
			for (int num = 0; num < list.Count; num++)
			{
				Thunderstore.PendingOffer pendingOffer = list[num];
				string key = pendingOffer.key;
				string label = pendingOffer.label;
				list2.Add(new Entry(label, delegate
				{
					ShowStoreDownload(key, label);
				}));
			}
			for (int num2 = 0; num2 < pendingConsentTransfers.Count; num2++)
			{
				FileTransfer transfer = pendingConsentTransfers[num2];
				string fileName = Path.GetFileName(transfer.fileName);
				list2.Add(new Entry(fileName, delegate
				{
					ShowDownload(transfer);
				}));
			}
			ShowWheelPage(list2, downloadPage, delegate(int page)
			{
				downloadPage = page;
				ShowDownloadPage();
			}, ShowHub, "No downloads");
		}

		private static void ShowStoreDownload(string key, string label)
		{
			Show(PageBack(ShowDownloads, new Entry("Accept Thunderstore", delegate
			{
				Thunderstore.AcceptPending(key);
				GameNotify.Show("Accepted " + label, 3f);
				ShowDownloads();
			}), new Entry("Decline", delegate
			{
				Thunderstore.DeclinePending(key);
				GameNotify.Show("Declined " + label, 3f);
				ShowDownloads();
			})), animate: false);
		}

		private static void ShowDownload(FileTransfer transfer)
		{
			string name = Path.GetFileName(transfer.fileName);
			float num = (float)transfer.totalBytes / 1024f / 1024f;
			ushort id = transfer.id;
			long peer = transfer.peer;
			Show(PageBack(ShowDownloads, new Entry($"Accept {num:F1} MB", delegate
			{
				FileTransferManager.AcceptPending(peer, id);
				GameNotify.Show("Accepted " + name, 3f);
				ShowDownloads();
			}), new Entry("Decline", delegate
			{
				FileTransferManager.DenyPending(peer, id);
				GameNotify.Show("Declined " + name, 3f);
				ShowDownloads();
			})), animate: false);
		}

		private static void ShowWheelPage(List<Entry> items, int page, Action<int> setPage, Action back, string emptyLabel)
		{
			int pages = Math.Max(1, (Math.Max(items.Count, 1) + 5 - 1) / 5);
			if (page >= pages)
			{
				page = 0;
			}
			List<Entry> list = new List<Entry>();
			if (items.Count == 0)
			{
				list.Add(new Entry(emptyLabel, delegate
				{
				}));
			}
			else
			{
				for (int num = page * 5; num < items.Count; num++)
				{
					if (list.Count >= 5)
					{
						break;
					}
					list.Add(items[num]);
				}
			}
			if (pages > 1)
			{
				int prev = page;
				int next = page;
				list.Add(new Entry("Prev", delegate
				{
					if (prev > 0)
					{
						setPage(prev - 1);
					}
				}));
				list.Add(new Entry("Next", delegate
				{
					if (next < pages - 1)
					{
						setPage(next + 1);
					}
				}));
			}
			Show(PageBack(back, list.ToArray()), animate: false);
		}

		private static void ShowSettings()
		{
			Show(PageBack(ShowHub, new Entry(Client.nameTagsVisible ? "Names On" : "Names Off", delegate
			{
				Client.nameTagsVisible = !Client.nameTagsVisible;
				ShowSettings();
			}), new Entry("File Sync", ShowSync), new Entry("Banned", delegate
			{
				bannedPage = 0;
				ShowBanned();
			}), new Entry("Net " + NetStats.downKBps.ToString("F0") + "KB/s down, " + NetStats.upKBps.ToString("F0") + "KB/s up", ShowSettings, wide: true), new Entry("Debug", ShowDebug)), animate: false);
		}

		private static void ShowBanned()
		{
			List<Entry> list = new List<Entry>();
			foreach (Tuple<long, string> bannedUser in BanList.bannedUsers)
			{
				long id = bannedUser.Item1;
				string name = bannedUser.Item2;
				list.Add(new Entry("Unban " + name, delegate
				{
					BanList.UnbanUser(id, name);
					GameNotify.Show("Unbanned " + name + ".", 3f);
					ShowBanned();
				}));
			}
			ShowWheelPage(list, bannedPage, delegate(int page)
			{
				bannedPage = page;
				ShowBanned();
			}, ShowSettings, "Nobody banned");
		}

		private static void ShowDebug()
		{
			Show(PageBack(ShowSettings, new Entry(DebugLog.On ? "Debug Log On" : "Debug Log", delegate
			{
				DebugLog.Toggle();
				GameNotify.Show(DebugLog.On ? "Debug log on. Details go to MelonLoader/Latest.log." : "Debug log off.");
				ShowDebug();
			}), new Entry(PlayerRepresentation.RepresentationTestActive ? "Rep Test On" : "Representation Test", delegate
			{
				if (PlayerRepresentation.RepresentationTestActive)
				{
					PlayerRepresentation.StopRepresentationTest();
				}
				else
				{
					PlayerRepresentation.StartRepresentationTest();
				}
				ShowDebug();
			}), new Entry(PlayerRepresentation.debugFrozen ? "Dummy Frozen" : "Freeze Dummy", delegate
			{
				if (!PlayerRepresentation.RepresentationTestActive)
				{
					GameNotify.Show("Turn on the Representation Test first.", 3f);
				}
				else
				{
					PlayerRepresentation.debugFrozen = !PlayerRepresentation.debugFrozen;
					ShowDebug();
				}
			}), new Entry("NPCs At Dummy", delegate
			{
				GameNotify.Show(SoloDebug.SendNpcs(), 3f);
			}), new Entry("Sync Tests", ShowSyncTests), new Entry("Install Custom Content Dependencies", delegate
			{
				Thunderstore.InstallCustomContentDependencies();
				PopUpMenuView instance = PopUpMenuView.instance;
				if ((Object)(object)instance != (Object)null)
				{
					instance.Deactivate();
				}
			}, wide: true)), animate: false);
		}

		private static void ShowSyncTests()
		{
			Show(PageBack(ShowDebug, new Entry("Follow Test", delegate
			{
				GameNotify.Show(SoloDebug.StartFollowTest());
			}), new Entry("NPC Follow Test", delegate
			{
				GameNotify.Show(SoloDebug.StartNpcFollowTest());
			}), new Entry("Freeze Test", delegate
			{
				GameNotify.Show(SoloDebug.StartFreezeTest());
			}), new Entry("Hit Check", delegate
			{
				GameNotify.Show(SoloDebug.HitCheck(), 5f);
			}), new Entry("Turrets And Claw", delegate
			{
				GameNotify.Show(SoloDebug.TurretAndClaw(), 6f);
			}), new Entry("Thunderstore Check", delegate
			{
				GameNotify.Show(SoloDebug.ThunderstoreCheck(), 6f);
			}), new Entry("More Tests", ShowMoreTests)), animate: false);
		}

		private static void ShowMoreTests()
		{
			Show(PageBack(ShowSyncTests, new Entry("Fizzle Nearest Corpse", delegate
			{
				GameNotify.Show(SoloDebug.FizzleNearest(), 3f);
			}), new Entry("Restart Nearest NPC", delegate
			{
				GameNotify.Show(SoloDebug.RestartNearestNpc(), 3f);
			})), animate: false);
		}

		private static void ShowVoice()
		{
			Show(PageBack(ShowHub, new Entry(VoiceManager.micEnabled ? "Mic On" : "Mic Off", delegate
			{
				VoiceManager.micEnabled = !VoiceManager.micEnabled;
				ShowVoice();
			}), new Entry((VoiceManager.mode == VoiceMode.Global) ? "Global" : "Proximity", delegate
			{
				VoiceManager.mode = ((VoiceManager.mode != VoiceMode.Global) ? VoiceMode.Global : VoiceMode.Proximity);
				VoiceManager.ApplySettings();
				ShowVoice();
			}), new Entry($"Range {VoiceManager.proximityRange}m", delegate
			{
				VoiceManager.proximityRange += 5;
				if (VoiceManager.proximityRange > 60)
				{
					VoiceManager.proximityRange = 10;
				}
				VoiceManager.ApplySettings();
				ShowVoice();
			}), new Entry($"Vol {VoiceManager.outputVolume}%", delegate
			{
				VoiceManager.outputVolume += 10;
				if (VoiceManager.outputVolume > 200)
				{
					VoiceManager.outputVolume = 0;
				}
				VoiceManager.ApplySettings();
				ShowVoice();
			}), new Entry("Mic Help", delegate
			{
				GameNotify.Show("Voice Chat uses Steam Mic. Change in Steam Settings under Voice > Voice Input Device", 8f);
			})), animate: false);
		}

		private static void ShowSync()
		{
			Show(PageBack(ShowSettings, new Entry(SyncPrefs.itemSyncEnabled.Value ? "Items On" : "Items Off", delegate
			{
				SyncPrefs.itemSyncEnabled.Value = !SyncPrefs.itemSyncEnabled.Value;
				ShowSync();
			}), new Entry(SyncPrefs.playermodelSyncEnabled.Value ? "Models On" : "Models Off", delegate
			{
				SyncPrefs.playermodelSyncEnabled.Value = !SyncPrefs.playermodelSyncEnabled.Value;
				ShowSync();
			}), new Entry(SyncPrefs.mapSyncEnabled.Value ? "Maps On" : "Maps Off", delegate
			{
				SyncPrefs.mapSyncEnabled.Value = !SyncPrefs.mapSyncEnabled.Value;
				ShowSync();
			}), new Entry(SyncPrefs.requireDownloadConsent.Value ? "Ask First" : "Auto Download", delegate
			{
				SyncPrefs.requireDownloadConsent.Value = !SyncPrefs.requireDownloadConsent.Value;
				ShowSync();
			}), new Entry($"Max {SyncPrefs.maxSyncSizeKB.Value / 1024}MB", delegate
			{
				int num = SyncPrefs.maxSyncSizeKB.Value / 1024 + 10;
				if (num > 200)
				{
					num = 10;
				}
				SyncPrefs.maxSyncSizeKB.Value = num * 1024;
				ShowSync();
			})), animate: false);
		}

		private static string Fit(string text, bool wide = false)
		{
			if (string.IsNullOrEmpty(text))
			{
				return " ";
			}
			text = text.Replace("\n", " ").Trim();
			int num = (wide ? 40 : 18);
			if (text.Length > num)
			{
				return text.Substring(0, num - 1) + "...";
			}
			return text;
		}
	}
}
namespace Entanglement.Representation
{
	public class PlayerRepresentation
	{
		private class GhostPart
		{
			public Transform source;

			public Transform part;

			public MeshFilter filter;

			public SkinnedMeshRenderer skinned;

			public Mesh baked;

			public Vector3 localPos;

			public Quaternion localRot = Quaternion.identity;
		}

		private class HeldGhost
		{
			public GameObject proxy;

			public GameObject source;

			public readonly List<GhostPart> parts = new List<GhostPart>();
		}

		public static float legJitter = 10f;

		public static Dictionary<long, PlayerRepresentation> representations = new Dictionary<long, PlayerRepresentation>();

		public static Transform[] syncedPoints = (Transform[])(object)new Transform[3];

		public static Transform syncedRoot;

		public Transform[] repTransforms = (Transform[])(object)new Transform[3];

		public Transform repRoot;

		public GameObject repFord;

		public Material repHologram;

		public GameObject repCanvas;

		public Canvas repCanvasComponent;

		public Transform repCanvasTransform;

		private CanvasGroup repCanvasGroup;

		public TextMeshProUGUI repNameText;

		public RawImage repTalkingIcon;

		public Transform repSkull;

		public Transform repGeo;

		public Transform repSHJnt;

		public Collider[] colliders = (Collider[])(object)new Collider[0];

		private Collider[] modelColliders = (Collider[])(object)new Collider[0];

		private bool enemyTargetReady;

		private GameObject enemyTarget;

		private TriggerRefProxy enemyTargetProxy;

		public SLZ_Body repBody;

		public SLZ_Body ragdollBody;

		public CharacterAnimationManager repAnimationManager;

		public GunSFX repGunSFX;

		public GunSFX repBalloonSFX;

		public GunSFX repStabSFX;

		public GravGunSFX repPowerPunchSFX;

		public Animator repAnimator;

		public Animator skinAnimator;

		public Animator activeAnimator;

		public GameObject currentSkinObject;

		public AssetBundle currentSkinBundle;

		public string currentSkinPath;

		public string loadingSkinPath;

		public int skinLoadGeneration;

		public bool isCustomSkinned;

		public Vector3 repInputVel = Vector3.zero;

		public Vector3 repSavedVel = Vector3.zero;

		public Vector3 prevRepRootPos = Vector3.zero;

		public string playerName;

		public long playerId;

		public bool isGrounded;

		public bool hasNetTarget;

		public Vector3 netRootPosition;

		public Vector3 netRootVelocity;

		public float netReceiveTime;

		public Vector3[] netPositions = (Vector3[])(object)new Vector3[3];

		public Quaternion[] netRotations = (Quaternion[])(object)new Quaternion[3];

		public Vector3[] netLimbVelocities = (Vector3[])(object)new Vector3[3];

		private Transform[] posedBones;

		private Vector3[] posePrev;

		private Vector3[] poseCurr;

		private Quaternion[] rotPrev;

		private Quaternion[] rotCurr;

		private bool hasSolvedPose;

		private bool heldByLocalPlayer;

		private bool grabCollisionIgnored;

		private float heldReleaseUntil;

		public const float repFollowSharpness = 28f;

		public const float repLimbSharpness = 48f;

		public const float repExtrapolationLimit = 0.28f;

		public const float repSnapDistance = 2.5f;

		public const float repMaxPredictedSpeed = 18f;

		public const float crouchVerticalDelta = 0.06f;

		public const float crouchLateralMax = 0.45f;

		public const float heldMaxPlanarStep = 0.1f;

		public const float heldMaxVerticalStep = 0.018f;

		public const float heldReleaseCooldown = 0.18f;

		public const float localHoldVerticalDamp = 2.5f;

		public static PlayerRepresentation debugRepresentation;

		public static float representationTestOffset = 1.5f;

		public const long TesterRepId = -2L;

		private const int MaxGhostParts = 64;

		private static readonly HeldGhost[] debugGhosts = new HeldGhost[3]
		{
			null,
			new HeldGhost(),
			new HeldGhost()
		};

		private static readonly Vector3[] debugPosScratch = (Vector3[])(object)new Vector3[3];

		private static readonly Quaternion[] debugRotScratch = (Quaternion[])(object)new Quaternion[3];

		public static bool debugFrozen;

		public static AssetBundle playerRepBundle;

		public bool previewOnly;

		public string lastCreateError;

		private static readonly Vector3 ParkSpot = new Vector3(0f, -10000f, 0f);

		private const float StaleAfter = 3f;

		private bool parked;

		private float lastPacketReal;

		private const float LoadFade = 0.8f;

		private const float LoadQuiet = 1f;

		private const float LoadHoldMax = 20f;

		private List<Renderer> awayHidden;

		private bool awayForLoad;

		private float awaySince;

		private static float lastLocalLoad = -10f;

		private const float DeadHoldMax = 6f;

		private const float DeathFade = 1f;

		private const float BackFade = 0.8f;

		private const float ArriveFade = 1f;

		private const float LeaveFade = 1f;

		private float deadUntil;

		private List<Renderer> deadHidden;

		private RepFade fade;

		private const float OverlapEnter = 0.35f;

		private const float OverlapLeave = 1.1f;

		private const float OverlapHeight = 1.8f;

		private const float AppearWindow = 1f;

		private const fl