Decompiled source of PeakStranding v0.9.0

plugins/com.github.wafflecomposite.PeakStranding.dll

Decompiled 32 minutes ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PeakStranding.Components;
using PeakStranding.Data;
using PeakStranding.Online;
using Photon.Pun;
using Photon.Realtime;
using Steamworks;
using UnityEngine;
using UnityEngine.SceneManagement;
using Zorro.Core;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("com.github.wafflecomposite.PeakStranding")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.9.0.0")]
[assembly: AssemblyInformationalVersion("0.9.0+9819bb06b56722c9c8ebcfd5fd62ba6dedefb9a6")]
[assembly: AssemblyProduct("com.github.wafflecomposite.PeakStranding")]
[assembly: AssemblyTitle("PeakStranding")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.9.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
public class BiDictionary : IEnumerable<KeyValuePair<string, string>>, IEnumerable
{
	private readonly Dictionary<string, string> _forward = new Dictionary<string, string>();

	private readonly Dictionary<string, string> _reverse = new Dictionary<string, string>();

	public void Add(string first, string second)
	{
		if (_forward.ContainsKey(first) || _reverse.ContainsKey(second))
		{
			throw new ArgumentException("Duplicate key or value.");
		}
		_forward[first] = second;
		_reverse[second] = first;
	}

	public bool TryGetByFirst(string first, out string second)
	{
		return _forward.TryGetValue(first, out second);
	}

	public bool TryGetBySecond(string second, out string first)
	{
		return _reverse.TryGetValue(second, out first);
	}

	public bool RemoveByFirst(string first)
	{
		if (!_forward.TryGetValue(first, out string value))
		{
			return false;
		}
		_forward.Remove(first);
		_reverse.Remove(value);
		return true;
	}

	public bool RemoveBySecond(string second)
	{
		if (!_reverse.TryGetValue(second, out string value))
		{
			return false;
		}
		_reverse.Remove(second);
		_forward.Remove(value);
		return true;
	}

	public List<string> GetAllFirsts()
	{
		return new List<string>(_forward.Keys);
	}

	public List<string> GetAllSeconds()
	{
		return new List<string>(_reverse.Keys);
	}

	public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
	{
		return _forward.GetEnumerator();
	}

	IEnumerator IEnumerable.GetEnumerator()
	{
		return GetEnumerator();
	}

	internal string TryGetByFirst(string prefabName)
	{
		throw new NotImplementedException();
	}
}
public class RestoredInteractable : MonoBehaviour, IInteractible
{
	public string RestoredName = "Restored Interactable";

	public string GetName()
	{
		return RestoredName;
	}

	public bool IsInteractible(Character interactor)
	{
		return true;
	}

	public void Interact(Character interactor)
	{
	}

	public void HoverEnter()
	{
	}

	public void HoverExit()
	{
	}

	public Vector3 Center()
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		return ((Component)this).transform.position;
	}

	public Transform GetTransform()
	{
		return ((Component)this).transform;
	}

	public string GetInteractionText()
	{
		return string.Empty;
	}
}
namespace BepInEx
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class BepInAutoPluginAttribute : Attribute
	{
		public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace BepInEx.Preloader.Core.Patching
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class PatcherAutoPluginAttribute : Attribute
	{
		public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace PeakStranding
{
	[BepInPlugin("com.github.wafflecomposite.PeakStranding", "PeakStranding", "0.9.0")]
	public class Plugin : BaseUnityPlugin, IOnEventCallback
	{
		internal static ConfigEntry<bool> saveStructuresLocallyConfig;

		internal static ConfigEntry<bool> loadLocalStructuresConfig;

		internal static ConfigEntry<int> localStructuresLimitConfig;

		internal static ConfigEntry<bool> sendStructuresToRemoteConfig;

		internal static ConfigEntry<bool> loadRemoteStructuresConfig;

		internal static ConfigEntry<int> remoteStructuresLimitConfig;

		internal static ConfigEntry<string> remoteApiUrlConfig;

		internal static ConfigEntry<bool> showStructureCreditsConfig;

		internal static ConfigEntry<bool> ropeOptimizerExperimentalConfig;

		internal static ConfigEntry<string> structureAllowListConfig;

		internal static ConfigEntry<bool> allowClientsLikeConfig;

		internal static ConfigEntry<bool> allowClientsDeleteConfig;

		public const string Id = "com.github.wafflecomposite.PeakStranding";

		internal static ManualLogSource Log { get; private set; }

		public static bool CfgLocalSaveStructures => saveStructuresLocallyConfig.Value;

		public static bool CfgLocalLoadStructures => loadLocalStructuresConfig.Value;

		public static int CfgLocalStructuresLimit => localStructuresLimitConfig.Value;

		public static bool CfgRemoteSaveStructures => sendStructuresToRemoteConfig.Value;

		public static bool CfgRemoteLoadStructures => loadRemoteStructuresConfig.Value;

		public static int CfgRemoteStructuresLimit => remoteStructuresLimitConfig.Value;

		public static string CfgRemoteApiUrl => remoteApiUrlConfig.Value;

		public static bool CfgShowStructureCredits => showStructureCreditsConfig.Value;

		public static bool CfgRopeOptimizerExperimental => ropeOptimizerExperimentalConfig.Value;

		public static string CfgStructureAllowList => structureAllowListConfig.Value;

		public static bool CfgAllowClientLike => allowClientsLikeConfig.Value;

		public static bool CfgAllowClientDelete => allowClientsDeleteConfig.Value;

		public static string Name => "PeakStranding";

		public static string Version => "0.9.0";

		private void Awake()
		{
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			saveStructuresLocallyConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Local", "Save_Structures_Locally", true, "Whether to save structures placed in your lobby locally");
			loadLocalStructuresConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Local", "Load_Local_Structures", false, "Whether to load previously saved structures at the start of a new run");
			localStructuresLimitConfig = ((BaseUnityPlugin)this).Config.Bind<int>("Local", "Local_Structures_Limit", -1, "How many local structures to load at the start of a new run (-1 for no limit). If you have more than this, only the most recent ones will be loaded.");
			sendStructuresToRemoteConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Online", "Send_Structures_To_Online", true, "Whether to send structures placed in your lobby to other players");
			loadRemoteStructuresConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Online", "Load_Online_Structures", true, "Whether to load random structures placed by other players");
			remoteStructuresLimitConfig = ((BaseUnityPlugin)this).Config.Bind<int>("Online", "Online_Structures_Limit", 40, "How many remote structures to load at the start of a new run");
			showStructureCreditsConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "Show_Structure_Credits", true, "Whether to show usernames for structures placed by other players in the UI");
			structureAllowListConfig = ((BaseUnityPlugin)this).Config.Bind<string>("Online", "Structure_Allow_List", string.Join(" ", DataHelper.prefabMapping.GetAllSeconds()), "A space-separated list of structure prefab names that are allowed to be placed by other players. Leave empty to allow all structures.");
			ropeOptimizerExperimentalConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Experimental", "Experimental_Rope_Optimizer", true, "Enable experimental optimizations for the ropes.");
			remoteApiUrlConfig = ((BaseUnityPlugin)this).Config.Bind<string>("Online", "Custom_Server_Api_BaseUrl", "", "Custom Server URL. Leave empty to use official Peak Stranding server");
			allowClientsLikeConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Online", "Allow_Clients_Like", true, "Allow clients to like structures.");
			allowClientsDeleteConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Online", "Allow_Clients_Delete", true, "Allow clients to delete structures.");
			PhotonNetwork.AddCallbackTarget((object)this);
			Log.LogInfo((object)("Plugin " + Name + " is patching..."));
			Harmony val = new Harmony("com.github.wafflecomposite.PeakStranding");
			val.PatchAll();
			Log.LogInfo((object)("Plugin " + Name + " is loaded!"));
			List<string> allSeconds = DataHelper.prefabMapping.GetAllSeconds();
			if (string.IsNullOrWhiteSpace(CfgStructureAllowList))
			{
				return;
			}
			string[] array = CfgStructureAllowList.ToLower().Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
			string[] array2 = array;
			foreach (string text in array2)
			{
				if (!allSeconds.Contains(text))
				{
					Log.LogWarning((object)("Unrecognized prefab name in allow list: '" + text + "'. Please check your structure allow list."));
				}
			}
		}

		private void OnDestroy()
		{
			PhotonNetwork.RemoveCallbackTarget((object)this);
		}

		public void OnEvent(EventData photonEvent)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: 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_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: 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_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			if (photonEvent.Code != 202)
			{
				return;
			}
			Hashtable val = (Hashtable)photonEvent.CustomData;
			string prefabName = (string)val[(byte)0];
			Vector3 val2 = (Vector3)(val.ContainsKey((byte)1) ? ((Vector3)val[(byte)1]) : Vector3.zero);
			Quaternion rotation = (Quaternion)(val.ContainsKey((byte)2) ? ((Quaternion)val[(byte)2]) : Quaternion.identity);
			object[] array = (val.ContainsKey((byte)5) ? ((object[])val[(byte)5]) : null);
			if (array == null || array.Length == 0 || !(array[0] as string == "PEAK_STRANDING_RESTORED"))
			{
				string[] array2 = new string[7] { "0_Items/ClimbingSpikeHammered", "0_Items/ShelfShroomSpawn", "0_Items/BounceShroomSpawn", "Flag_planted_seagull", "Flag_planted_turtle", "PortableStovetop_Placed", "ScoutCannon_Placed" };
				if (Array.Exists(array2, (string p) => p == prefabName))
				{
					Log.LogInfo((object)$"Player {photonEvent.Sender} placed {prefabName} at {val2}, saving it...");
					PlacedItemData placedItemData = new PlacedItemData
					{
						PrefabName = prefabName,
						Position = val2,
						Rotation = rotation
					};
					placedItemData.AddCurrentRunContext();
					SaveManager.SaveItem(placedItemData);
				}
			}
		}
	}
	public static class SaveManager
	{
		[CompilerGenerated]
		private sealed class <>c__DisplayClass20_0
		{
			public PlacedItemData itemData;

			public object[] instantiationData;

			public Action<GameObject> onSpawned;

			public Rope rope;

			public string anchorPrefabName;

			public float seg;

			public RopeSpool spool;

			public GameObject ropeObj;

			public GameObject spoolObj;
		}

		[CompilerGenerated]
		private sealed class <SetAntigravWhenReady>d__21 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public RopeAnchorWithRope rapw;

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

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

			[DebuggerHidden]
			public <SetAntigravWhenReady>d__21(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					break;
				case 1:
					<>1__state = -1;
					break;
				}
				if ((Object)(object)rapw.rope == (Object)null)
				{
					<>2__current = null;
					<>1__state = 1;
					return true;
				}
				rapw.rope.antigrav = true;
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		public const string RESTORED_ITEM_MARKER = "PEAK_STRANDING_RESTORED";

		private static readonly List<PlacedItemData> SessionPlacedItems = new List<PlacedItemData>();

		private static readonly JsonSerializerSettings JsonSettings = new JsonSerializerSettings
		{
			Converters = new List<JsonConverter>
			{
				(JsonConverter)(object)new Vector3Converter(),
				(JsonConverter)(object)new QuaternionConverter()
			}
		};

		private static readonly Dictionary<int, List<(PlacedItemData data, string label, ulong id, int likes, ulong user_id)>> CachedStructures = new Dictionary<int, List<(PlacedItemData, string, ulong, int, ulong)>>();

		private static readonly Dictionary<int, List<GameObject>> SpawnedInstances = new Dictionary<int, List<GameObject>>();

		public static bool IsRestoring { get; private set; }

		public static void BeginRestore()
		{
			IsRestoring = true;
		}

		public static void EndRestore()
		{
			IsRestoring = false;
		}

		private static string GetSaveFilePath(int mapId)
		{
			string text = Path.Combine(Paths.ConfigPath, "PeakStranding", "PlacedItems");
			Directory.CreateDirectory(text);
			return Path.Combine(text, $"PlacedItems_{mapId}.json");
		}

		public static void ClearCache()
		{
			SessionPlacedItems.Clear();
			CachedStructures.Clear();
			foreach (int item in SpawnedInstances.Keys.ToList())
			{
				DespawnStructuresForSegment(item);
			}
			SpawnedInstances.Clear();
		}

		public static void SaveItem(PlacedItemData data)
		{
			if ((!Plugin.CfgLocalSaveStructures && !Plugin.CfgRemoteSaveStructures) || data == null || !PhotonNetwork.IsMasterClient)
			{
				return;
			}
			SessionPlacedItems.Add(data);
			int currentLevelIndex = DataHelper.GetCurrentLevelIndex();
			if (Plugin.CfgLocalSaveStructures)
			{
				string saveFilePath = GetSaveFilePath(currentLevelIndex);
				List<PlacedItemData> savedItemsForSeed = GetSavedItemsForSeed(currentLevelIndex);
				savedItemsForSeed.Add(data);
				string contents = JsonConvert.SerializeObject((object)savedItemsForSeed, (Formatting)1, JsonSettings);
				File.WriteAllText(saveFilePath, contents);
				Plugin.Log.LogInfo((object)$"Saved local item {data.PrefabName}. Total items for map {currentLevelIndex}: {savedItemsForSeed.Count}");
			}
			if (!Plugin.CfgRemoteSaveStructures)
			{
				return;
			}
			try
			{
				RemoteApi.Upload(currentLevelIndex, data);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Failed to upload item " + data.PrefabName + ": " + ex.Message));
			}
		}

		private static List<PlacedItemData> GetSavedItemsForSeed(int mapId)
		{
			string saveFilePath = GetSaveFilePath(mapId);
			if (File.Exists(saveFilePath))
			{
				string text = File.ReadAllText(saveFilePath);
				return JsonConvert.DeserializeObject<List<PlacedItemData>>(text, JsonSettings) ?? new List<PlacedItemData>();
			}
			return new List<PlacedItemData>();
		}

		public static void CacheLocalStructures()
		{
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			if (!PhotonNetwork.IsMasterClient)
			{
				return;
			}
			int currentLevelIndex = DataHelper.GetCurrentLevelIndex();
			List<PlacedItemData> savedItemsForSeed = GetSavedItemsForSeed(currentLevelIndex);
			List<PlacedItemData> list = savedItemsForSeed;
			if (Plugin.CfgLocalStructuresLimit > 0 && savedItemsForSeed.Count > Plugin.CfgLocalStructuresLimit)
			{
				list = savedItemsForSeed.Skip(savedItemsForSeed.Count - Plugin.CfgLocalStructuresLimit).ToList();
			}
			Plugin.Log.LogInfo((object)$"Caching {list.Count} local items for map {currentLevelIndex}.");
			foreach (PlacedItemData item in list)
			{
				if (!CachedStructures.ContainsKey(item.MapSegment))
				{
					CachedStructures[item.MapSegment] = new List<(PlacedItemData, string, ulong, int, ulong)>();
				}
				CachedStructures[item.MapSegment].Add((item, "You", 0uL, 0, SteamUser.GetSteamID().m_SteamID));
			}
		}

		public static void CacheRemoteStructures(List<ServerStructureDto> items)
		{
			if (!PhotonNetwork.IsMasterClient)
			{
				return;
			}
			if (items == null || items.Count == 0)
			{
				Plugin.Log.LogInfo((object)"No remote structures to cache.");
				return;
			}
			foreach (ServerStructureDto item in items)
			{
				PlacedItemData placedItemData = item.ToPlacedItemData();
				if (!CachedStructures.ContainsKey(placedItemData.MapSegment))
				{
					CachedStructures[placedItemData.MapSegment] = new List<(PlacedItemData, string, ulong, int, ulong)>();
				}
				CachedStructures[placedItemData.MapSegment].Add((placedItemData, item.username, item.id, item.likes, item.user_id));
			}
		}

		public static void SpawnStructuresForSegment(int segmentIndex)
		{
			if (!PhotonNetwork.IsMasterClient)
			{
				return;
			}
			if (!CachedStructures.TryGetValue(segmentIndex, out List<(PlacedItemData, string, ulong, int, ulong)> value))
			{
				Plugin.Log.LogInfo((object)$"No cached structures to spawn for segment {segmentIndex}.");
				return;
			}
			BeginRestore();
			try
			{
				Plugin.Log.LogInfo((object)$"Spawning {value.Count} structures for segment {segmentIndex}.");
				foreach (var item in value)
				{
					var (itemData, label, id, likes, user_id) = item;
					SpawnItem(itemData, label, delegate(GameObject spawnedGo)
					{
						if ((Object)(object)spawnedGo == (Object)null)
						{
							Plugin.Log.LogWarning((object)"SpawnItem resulted in a null GameObject. Skipping registration.");
						}
						else
						{
							if (!SpawnedInstances.ContainsKey(segmentIndex))
							{
								SpawnedInstances[segmentIndex] = new List<GameObject>();
							}
							SpawnedInstances[segmentIndex].Add(spawnedGo);
							if (!((Object)(object)spawnedGo.GetComponent<Rope>() != (Object)null) || !((Object)(object)spawnedGo.GetComponent<RopeAnchor>() == (Object)null) || !((Object)(object)spawnedGo.GetComponent<RopeAnchorWithRope>() == (Object)null))
							{
								OverlayManager.RegisterInfo info = default(OverlayManager.RegisterInfo);
								info.target = spawnedGo;
								info.username = label;
								info.likes = likes;
								info.id = id;
								info.user_id = user_id;
								info.canLike = true;
								OverlayManager.Register(info);
								PeakStrandingSyncManager.Instance?.RegisterNewStructure(spawnedGo, label, likes, id, user_id);
							}
						}
					});
				}
			}
			finally
			{
				EndRestore();
			}
		}

		public static void DespawnStructuresForSegment(int segmentIndex)
		{
			if (!PhotonNetwork.IsMasterClient)
			{
				return;
			}
			if (!SpawnedInstances.TryGetValue(segmentIndex, out List<GameObject> value))
			{
				Plugin.Log.LogInfo((object)$"No spawned instances to despawn for segment {segmentIndex}.");
				return;
			}
			Plugin.Log.LogInfo((object)$"Despawning {value.Count} structures for segment {segmentIndex}.");
			foreach (GameObject item in value)
			{
				if ((Object)(object)item != (Object)null)
				{
					DeletionUtility.Delete(item);
				}
			}
			SpawnedInstances.Remove(segmentIndex);
		}

		public static void CleanupRunStructures()
		{
			//IL_0019: 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_0021: 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)
			if (!PhotonNetwork.IsMasterClient)
			{
				return;
			}
			Plugin.Log.LogInfo((object)"Cleaning up all structures from the run.");
			int num = 0;
			ValueIterator<int, PhotonView> enumerator = PhotonNetwork.PhotonViewCollection.GetEnumerator();
			try
			{
				while (enumerator.MoveNext())
				{
					PhotonView current = enumerator.Current;
					if (!((Object)(object)current == (Object)null) && current.InstantiationData != null && current.InstantiationData.Length != 0 && current.InstantiationData[0] is string text && text == "PEAK_STRANDING_RESTORED")
					{
						PhotonNetwork.Destroy(((Component)current).gameObject);
						num++;
					}
				}
			}
			finally
			{
				((IDisposable)enumerator).Dispose();
			}
			Plugin.Log.LogInfo((object)$"Cleaned up {num} spawned structures and their buffered RPCs.");
			SpawnedInstances.Clear();
			CachedStructures.Clear();
			SessionPlacedItems.Clear();
		}

		public static void SpawnItem(PlacedItemData itemData, string label = "", Action<GameObject>? onSpawned = null)
		{
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: 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_04d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_04da: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0228: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_051e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0523: Unknown result type (might be due to invalid IL or missing references)
			//IL_0528: Unknown result type (might be due to invalid IL or missing references)
			//IL_052d: Unknown result type (might be due to invalid IL or missing references)
			//IL_054a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0557: Unknown result type (might be due to invalid IL or missing references)
			//IL_0378: Unknown result type (might be due to invalid IL or missing references)
			//IL_0383: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0277: Unknown result type (might be due to invalid IL or missing references)
			//IL_0282: Unknown result type (might be due to invalid IL or missing references)
			//IL_02af: Unknown result type (might be due to invalid IL or missing references)
			//IL_02df: Unknown result type (might be due to invalid IL or missing references)
			<>c__DisplayClass20_0 CS$<>8__locals0 = new <>c__DisplayClass20_0();
			CS$<>8__locals0.itemData = itemData;
			CS$<>8__locals0.onSpawned = onSpawned;
			string prefabName = CS$<>8__locals0.itemData.PrefabName;
			if (!DataHelper.IsPrefabAllowed(prefabName))
			{
				Plugin.Log.LogError((object)("Prefab '" + prefabName + "' is not allowed by the structure allow list."));
				CS$<>8__locals0.onSpawned?.Invoke(null);
				return;
			}
			CS$<>8__locals0.instantiationData = new object[1] { "PEAK_STRANDING_RESTORED" };
			if (!prefabName.StartsWith("PeakStranding/"))
			{
				GameObject val = Resources.Load<GameObject>(prefabName);
				if ((Object)(object)val == (Object)null)
				{
					Plugin.Log.LogError((object)("Prefab not found in Resources: " + prefabName));
					CS$<>8__locals0.onSpawned?.Invoke(null);
					return;
				}
				GameObject val2 = PhotonNetwork.Instantiate(prefabName, CS$<>8__locals0.itemData.Position, CS$<>8__locals0.itemData.Rotation, (byte)0, CS$<>8__locals0.instantiationData);
				if ((Object)(object)val2 == (Object)null)
				{
					Plugin.Log.LogError((object)("Failed to instantiate prefab via Photon: " + prefabName));
					CS$<>8__locals0.onSpawned?.Invoke(null);
				}
				else
				{
					val2.AddComponent<RestoredItem>();
					CS$<>8__locals0.onSpawned?.Invoke(val2);
				}
			}
			else if (prefabName.StartsWith("PeakStranding/JungleVine"))
			{
				GameObject val3 = PhotonNetwork.Instantiate("ChainShootable", CS$<>8__locals0.itemData.RopeStart, Quaternion.identity, (byte)0, CS$<>8__locals0.instantiationData);
				CS$<>8__locals0.onSpawned?.Invoke(val3);
				JungleVine component = val3.GetComponent<JungleVine>();
				if ((Object)(object)component != (Object)null)
				{
					component.photonView.RPC("ForceBuildVine_RPC", (RpcTarget)3, new object[4]
					{
						CS$<>8__locals0.itemData.RopeStart,
						CS$<>8__locals0.itemData.RopeEnd,
						CS$<>8__locals0.itemData.RopeLength,
						CS$<>8__locals0.itemData.RopeFlyingRotation
					});
				}
				else
				{
					Plugin.Log.LogError((object)"Failed to instantiate JungleVine prefab: vine == null");
				}
			}
			else if (prefabName.StartsWith("PeakStranding/RopeShooter"))
			{
				string text = (CS$<>8__locals0.itemData.RopeAntiGrav ? "RopeAnchorForRopeShooterAnti" : "RopeAnchorForRopeShooter");
				GameObject val4 = PhotonNetwork.Instantiate(text, CS$<>8__locals0.itemData.RopeStart, CS$<>8__locals0.itemData.RopeAnchorRotation, (byte)0, CS$<>8__locals0.instantiationData);
				CS$<>8__locals0.onSpawned?.Invoke(val4);
				RopeAnchorProjectile component2 = val4.GetComponent<RopeAnchorProjectile>();
				if ((Object)(object)component2 == (Object)null)
				{
					Plugin.Log.LogError((object)"Failed to instantiate Rope: Prefab lacks RopeAnchorProjectile");
					return;
				}
				float num = Vector3.Distance(CS$<>8__locals0.itemData.RopeStart, CS$<>8__locals0.itemData.RopeEnd) * 0.01f;
				component2.photonView.RPC("GetShot", (RpcTarget)3, new object[4]
				{
					CS$<>8__locals0.itemData.RopeEnd,
					num,
					CS$<>8__locals0.itemData.RopeLength,
					CS$<>8__locals0.itemData.RopeFlyingRotation
				});
				if (CS$<>8__locals0.itemData.RopeAntiGrav)
				{
					RopeAnchorWithRope component3 = val4.GetComponent<RopeAnchorWithRope>();
					if ((Object)(object)component3 != (Object)null)
					{
						((MonoBehaviour)component2).StartCoroutine(SetAntigravWhenReady(component3));
					}
				}
			}
			else if (prefabName.StartsWith("PeakStranding/RopeSpool"))
			{
				string text2 = (CS$<>8__locals0.itemData.RopeAntiGrav ? "0_Items/Anti-Rope Spool" : "0_Items/RopeSpool");
				CS$<>8__locals0.anchorPrefabName = (CS$<>8__locals0.itemData.RopeAntiGrav ? "RopeAnchorAnti" : "RopeAnchor");
				CS$<>8__locals0.spoolObj = PhotonNetwork.Instantiate(text2, CS$<>8__locals0.itemData.Position, CS$<>8__locals0.itemData.Rotation, (byte)0, CS$<>8__locals0.instantiationData);
				CS$<>8__locals0.spoolObj.AddComponent<RestoredItem>();
				CS$<>8__locals0.spool = CS$<>8__locals0.spoolObj.GetComponent<RopeSpool>();
				CS$<>8__locals0.ropeObj = PhotonNetwork.Instantiate(((Object)CS$<>8__locals0.spool.ropePrefab).name, CS$<>8__locals0.spool.ropeBase.position, CS$<>8__locals0.spool.ropeBase.rotation, (byte)0, CS$<>8__locals0.instantiationData);
				CS$<>8__locals0.ropeObj.AddComponent<RestoredItem>();
				CS$<>8__locals0.rope = CS$<>8__locals0.ropeObj.GetComponent<Rope>();
				((MonoBehaviourPun)CS$<>8__locals0.rope).photonView.RPC("AttachToSpool_Rpc", (RpcTarget)3, new object[1] { ((ItemComponent)CS$<>8__locals0.spool).photonView });
				CS$<>8__locals0.seg = Mathf.Max(CS$<>8__locals0.itemData.RopeLength, CS$<>8__locals0.spool.minSegments);
				CS$<>8__locals0.spool.Segments = CS$<>8__locals0.seg;
				CS$<>8__locals0.rope.Segments = CS$<>8__locals0.seg;
				if (CS$<>8__locals0.itemData.RopeAntiGrav)
				{
					CS$<>8__locals0.spool.isAntiRope = true;
					CS$<>8__locals0.rope.antigrav = true;
				}
				((MonoBehaviour)CS$<>8__locals0.spool).StartCoroutine(FinaliseRope());
			}
			else if (prefabName.StartsWith("PeakStranding/MagicBeanVine"))
			{
				GameObject val5 = PhotonNetwork.Instantiate("0_Items/MagicBean", CS$<>8__locals0.itemData.Position, Quaternion.identity, (byte)0, CS$<>8__locals0.instantiationData);
				val5.AddComponent<RestoredItem>();
				MagicBean component4 = val5.GetComponent<MagicBean>();
				if ((Object)(object)component4 == (Object)null)
				{
					Plugin.Log.LogError((object)"Failed to instantiate MagicBean: prefab missing MagicBean script");
					return;
				}
				Vector3 val6 = CS$<>8__locals0.itemData.Rotation * Vector3.forward;
				((ItemComponent)component4).photonView.RPC("GrowVineRPC", (RpcTarget)3, new object[3]
				{
					CS$<>8__locals0.itemData.Position,
					val6,
					CS$<>8__locals0.itemData.RopeLength
				});
				CS$<>8__locals0.onSpawned?.Invoke(val5);
			}
			else
			{
				Plugin.Log.LogError((object)("Failed to instantiate some prefab: Unhandled type: " + prefabName));
				CS$<>8__locals0.onSpawned?.Invoke(null);
			}
			[IteratorStateMachine(typeof(<>c__DisplayClass20_0.<<SpawnItem>g__FinaliseRope|0>d))]
			IEnumerator FinaliseRope()
			{
				//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
				return new <>c__DisplayClass20_0.<<SpawnItem>g__FinaliseRope|0>d(0)
				{
					<>4__this = CS$<>8__locals0
				};
			}
		}

		[IteratorStateMachine(typeof(<SetAntigravWhenReady>d__21))]
		private static IEnumerator SetAntigravWhenReady(RopeAnchorWithRope rapw)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <SetAntigravWhenReady>d__21(0)
			{
				rapw = rapw
			};
		}
	}
	public class Vector3Converter : JsonConverter<Vector3>
	{
		public override void WriteJson(JsonWriter writer, Vector3 value, JsonSerializer serializer)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			writer.WriteStartObject();
			writer.WritePropertyName("x");
			writer.WriteValue(value.x);
			writer.WritePropertyName("y");
			writer.WriteValue(value.y);
			writer.WritePropertyName("z");
			writer.WriteValue(value.z);
			writer.WriteEndObject();
		}

		public override Vector3 ReadJson(JsonReader reader, Type objectType, Vector3 existingValue, bool hasExistingValue, JsonSerializer serializer)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			JObject val = JObject.Load(reader);
			JToken obj = val["x"];
			float num = ((obj != null) ? Extensions.Value<float>((IEnumerable<JToken>)obj) : 0f);
			JToken obj2 = val["y"];
			float num2 = ((obj2 != null) ? Extensions.Value<float>((IEnumerable<JToken>)obj2) : 0f);
			JToken obj3 = val["z"];
			float num3 = ((obj3 != null) ? Extensions.Value<float>((IEnumerable<JToken>)obj3) : 0f);
			return new Vector3(num, num2, num3);
		}
	}
	public class QuaternionConverter : JsonConverter<Quaternion>
	{
		public override void WriteJson(JsonWriter writer, Quaternion value, JsonSerializer serializer)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			writer.WriteStartObject();
			writer.WritePropertyName("x");
			writer.WriteValue(value.x);
			writer.WritePropertyName("y");
			writer.WriteValue(value.y);
			writer.WritePropertyName("z");
			writer.WriteValue(value.z);
			writer.WritePropertyName("w");
			writer.WriteValue(value.w);
			writer.WriteEndObject();
		}

		public override Quaternion ReadJson(JsonReader reader, Type objectType, Quaternion existingValue, bool hasExistingValue, JsonSerializer serializer)
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			JObject val = JObject.Load(reader);
			JToken obj = val["x"];
			float num = ((obj != null) ? Extensions.Value<float>((IEnumerable<JToken>)obj) : 0f);
			JToken obj2 = val["y"];
			float num2 = ((obj2 != null) ? Extensions.Value<float>((IEnumerable<JToken>)obj2) : 0f);
			JToken obj3 = val["z"];
			float num3 = ((obj3 != null) ? Extensions.Value<float>((IEnumerable<JToken>)obj3) : 0f);
			JToken obj4 = val["w"];
			float num4 = ((obj4 != null) ? Extensions.Value<float>((IEnumerable<JToken>)obj4) : 0f);
			return new Quaternion(num, num2, num3, num4);
		}
	}
}
namespace PeakStranding.Patches
{
	[HarmonyPatch(typeof(GameOverHandler), "LoadAirportMaster")]
	public static class GameOverHandlerLoadAirportPatch
	{
		[HarmonyPrefix]
		private static void Prefix()
		{
			PeakStrandingSyncManager.DestroyInstance();
			if (PhotonNetwork.IsMasterClient)
			{
				Plugin.Log.LogInfo((object)"Run is over, cleaning up all spawned structures and buffered RPCs.");
				SaveManager.CleanupRunStructures();
			}
		}
	}
	[HarmonyPatch(typeof(JungleVine), "ForceBuildVine_RPC")]
	public class JungleVineForceBuildPatch
	{
		private static void Postfix(Vector3 from, Vector3 to, float hang, Vector3 mid)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			if (PhotonNetwork.IsMasterClient && !SaveManager.IsRestoring)
			{
				PlacedItemData placedItemData = new PlacedItemData
				{
					PrefabName = "PeakStranding/JungleVine",
					RopeStart = from,
					RopeEnd = to,
					RopeLength = hang,
					RopeFlyingRotation = mid
				};
				placedItemData.AddCurrentRunContext();
				SaveManager.SaveItem(placedItemData);
			}
		}
	}
	[HarmonyPatch(typeof(MagicBean), "GrowVineRPC")]
	public static class MagicBeanGrowVinePatch
	{
		private static readonly ConditionalWeakTable<MagicBean, object> saved = new ConditionalWeakTable<MagicBean, object>();

		private static readonly object s_token = new object();

		private static void Postfix(MagicBean __instance, Vector3 pos, Vector3 direction, float maxLength)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: 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)
			if (PhotonNetwork.IsMasterClient && !SaveManager.IsRestoring && !saved.TryGetValue(__instance, out object _))
			{
				saved.Add(__instance, s_token);
				PlacedItemData placedItemData = new PlacedItemData
				{
					PrefabName = "PeakStranding/MagicBeanVine",
					Position = pos,
					Rotation = Quaternion.LookRotation(direction),
					RopeLength = maxLength
				};
				placedItemData.AddCurrentRunContext();
				SaveManager.SaveItem(placedItemData);
			}
		}
	}
	[HarmonyPatch(typeof(MagicBean), "GrowVineRPC")]
	public static class MagicBean_GrowAndTrackVine_Patch
	{
		private static bool Prefix(MagicBean __instance, Vector3 pos, Vector3 direction, float maxLength)
		{
			//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_005c: Unknown result type (might be due to invalid IL or missing references)
			MagicBeanLink magicBeanLink = ((Component)__instance).GetComponent<MagicBeanLink>();
			RestoredItem component = ((Component)__instance).GetComponent<RestoredItem>();
			bool isRestoredBean = (Object)(object)component != (Object)null;
			if ((Object)(object)magicBeanLink != (Object)null && (Object)(object)magicBeanLink.vine != (Object)null)
			{
				return false;
			}
			if ((Object)(object)magicBeanLink == (Object)null)
			{
				magicBeanLink = ((Component)__instance).gameObject.AddComponent<MagicBeanLink>();
			}
			MagicBeanVine val = Object.Instantiate<MagicBeanVine>(__instance.plantPrefab, pos, Quaternion.identity);
			((Component)val).transform.up = direction;
			val.maxLength = maxLength;
			magicBeanLink.vine = val;
			magicBeanLink.isRestoredBean = isRestoredBean;
			return false;
		}
	}
	[HarmonyPatch(typeof(MapHandler), "GoToSegment")]
	public static class MapHandlerGoToSegmentPatch
	{
		[HarmonyPostfix]
		private static void Postfix(MapHandler __instance, Segment s)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Expected I4, but got Unknown
			if (PhotonNetwork.IsMasterClient)
			{
				int num = (int)s;
				int segmentIndex = num - 1;
				Plugin.Log.LogInfo((object)$"Map segment changing to {num}. Updating structures.");
				SaveManager.DespawnStructuresForSegment(segmentIndex);
				SaveManager.SpawnStructuresForSegment(num);
			}
		}
	}
	[HarmonyPatch(typeof(PhotonNetwork), "Instantiate")]
	public class PhotonInstantiatePatch
	{
		private static void Postfix(string prefabName, Vector3 position, Quaternion rotation, byte group, object[]? data = null)
		{
			//IL_00a1: 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_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			string prefabName2 = prefabName;
			if (PhotonNetwork.IsMasterClient && !SaveManager.IsRestoring && (data == null || data.Length == 0 || !(data[0] as string == "PEAK_STRANDING_RESTORED")))
			{
				string[] array = new string[7] { "0_Items/ClimbingSpikeHammered", "0_Items/ShelfShroomSpawn", "0_Items/BounceShroomSpawn", "Flag_planted_seagull", "Flag_planted_turtle", "PortableStovetop_Placed", "ScoutCannon_Placed" };
				if (Array.Exists(array, (string p) => p == prefabName2))
				{
					PlacedItemData placedItemData = new PlacedItemData
					{
						PrefabName = prefabName2,
						Position = position,
						Rotation = rotation
					};
					placedItemData.AddCurrentRunContext();
					SaveManager.SaveItem(placedItemData);
				}
			}
		}
	}
	[HarmonyPatch(typeof(RopeAnchorProjectile), "GetShot")]
	public static class RopeAnchorProjectileGetShotPatch
	{
		private static void Postfix(RopeAnchorProjectile __instance, Vector3 to, float travelTime, float ropeLength, Vector3 flyingRotation)
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: 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_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			if (PhotonNetwork.IsMasterClient && !SaveManager.IsRestoring)
			{
				RopeAnchorWithRope component = ((Component)__instance).GetComponent<RopeAnchorWithRope>();
				int num;
				if (component == null)
				{
					num = 0;
				}
				else
				{
					GameObject ropePrefab = component.ropePrefab;
					num = (((ropePrefab == null) ? null : ropePrefab.GetComponent<Rope>()?.antigrav).GetValueOrDefault() ? 1 : 0);
				}
				bool ropeAntiGrav = (byte)num != 0;
				PlacedItemData placedItemData = new PlacedItemData
				{
					PrefabName = "PeakStranding/RopeShooter",
					RopeStart = ((Component)__instance).transform.position,
					RopeEnd = to,
					RopeLength = ropeLength,
					RopeFlyingRotation = flyingRotation,
					RopeAnchorRotation = ((Component)__instance).transform.rotation,
					RopeAntiGrav = ropeAntiGrav
				};
				placedItemData.AddCurrentRunContext();
				SaveManager.SaveItem(placedItemData);
			}
		}
	}
	[HarmonyPatch(typeof(RopeAnchorWithRope), "SpawnRope")]
	public static class RopeAnchorWithRope_SpawnRope_Patch
	{
		private static void Postfix(RopeAnchorWithRope __instance, ref Rope __result)
		{
			if (!PhotonNetwork.IsMasterClient || (Object)(object)__instance == (Object)null)
			{
				return;
			}
			PhotonView photonView = ((MonoBehaviourPun)__instance).photonView;
			if (!((Object)(object)photonView == (Object)null))
			{
				DeletableGroup deletableGroup = ((Component)__instance).GetComponent<DeletableGroup>();
				if ((Object)(object)deletableGroup == (Object)null)
				{
					deletableGroup = ((Component)__instance).gameObject.AddComponent<DeletableGroup>();
				}
				deletableGroup.Add(photonView);
				if ((Object)(object)__result != (Object)null && (Object)(object)((MonoBehaviourPun)__result).photonView != (Object)null)
				{
					deletableGroup.Add(((MonoBehaviourPun)__result).photonView);
				}
				PhotonView pv = default(PhotonView);
				if ((Object)(object)__instance.anchor != (Object)null && ((Component)__instance.anchor).TryGetComponent<PhotonView>(ref pv))
				{
					deletableGroup.Add(pv);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Rope), "AttachToAnchor_Rpc")]
	public static class RopeAttachToAnchorPatch
	{
		private static readonly ConditionalWeakTable<Rope, object> saved = new ConditionalWeakTable<Rope, object>();

		private static readonly object s_token = new object();

		private static void Prefix(Rope __instance, PhotonView anchorView)
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: 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_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			if (!PhotonNetwork.IsMasterClient || SaveManager.IsRestoring || saved.TryGetValue(__instance, out object _))
			{
				return;
			}
			object? value2 = AccessTools.Field(typeof(Rope), "spool").GetValue(__instance);
			RopeSpool val = (RopeSpool)((value2 is RopeSpool) ? value2 : null);
			RopeAnchor val2 = default(RopeAnchor);
			if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).GetComponent<RestoredItem>() != (Object)null) && !((Object)(object)anchorView == (Object)null) && ((Component)anchorView).TryGetComponent<RopeAnchor>(ref val2) && !((Object)(object)val2 == (Object)null))
			{
				Vector3 position = ((Component)val2).transform.position;
				Quaternion rotation = ((Component)val2).transform.rotation;
				saved.Add(__instance, s_token);
				GameObject gameObject = ((Component)anchorView).gameObject;
				DeletableGroup deletableGroup = gameObject.GetComponent<DeletableGroup>();
				if ((Object)(object)deletableGroup == (Object)null)
				{
					deletableGroup = gameObject.AddComponent<DeletableGroup>();
				}
				deletableGroup.Add(anchorView);
				if ((Object)(object)__instance != (Object)null && (Object)(object)((MonoBehaviourPun)__instance).photonView != (Object)null)
				{
					deletableGroup.Add(((MonoBehaviourPun)__instance).photonView);
				}
				float ropeLength = ((val.Segments > 0.01f) ? val.Segments : Mathf.Max((float)__instance.SegmentCount, val.minSegments));
				PlacedItemData placedItemData = new PlacedItemData
				{
					PrefabName = "PeakStranding/RopeSpool",
					Position = ((Component)val).transform.position,
					Rotation = ((Component)val).transform.rotation,
					RopeLength = ropeLength,
					RopeAntiGrav = (val.isAntiRope || __instance.antigrav),
					RopeStart = val.ropeBase.position,
					RopeEnd = position,
					RopeAnchorRotation = rotation
				};
				placedItemData.AddCurrentRunContext();
				SaveManager.SaveItem(placedItemData);
			}
		}
	}
	[HarmonyPatch(typeof(RopeBoneVisualizer), "LateUpdate")]
	public static class RopeBoneVisualizer_LateUpdate_Patch
	{
		private static bool Prefix(RopeBoneVisualizer __instance)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			if (PhotonNetwork.IsMasterClient && Plugin.CfgRopeOptimizerExperimental)
			{
				Rope val = (Rope)AccessTools.Field(typeof(RopeBoneVisualizer), "rope").GetValue(__instance);
				if ((Object)(object)val != (Object)null && val.GetData().IsSleeping)
				{
					return false;
				}
				return true;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(RopeSegment), "Interact")]
	public static class RopeSegment_Interact_Patch
	{
		private static void Prefix(RopeSegment __instance)
		{
			if ((Object)(object)__instance.rope != (Object)null && Plugin.CfgRopeOptimizerExperimental)
			{
				__instance.rope.WakeUp();
			}
		}
	}
	public class RopeSleepManager : MonoBehaviourPunCallbacks
	{
		private static bool initialized;

		public static void Initialize()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			if (!initialized && Plugin.CfgRopeOptimizerExperimental)
			{
				GameObject val = new GameObject("RopeSleepManager");
				val.AddComponent<RopeSleepManager>();
				Object.DontDestroyOnLoad((Object)(object)val);
				initialized = true;
			}
		}

		public override void OnPlayerEnteredRoom(Player newPlayer)
		{
			if (!PhotonNetwork.IsMasterClient || !Plugin.CfgRopeOptimizerExperimental)
			{
				return;
			}
			Plugin.Log.LogError((object)("[RopeOptimizer] Player " + newPlayer.NickName + " entered. Waking up all ropes for synchronization."));
			Rope[] array = Object.FindObjectsByType<Rope>((FindObjectsSortMode)0);
			Rope[] array2 = array;
			foreach (Rope rope in array2)
			{
				if (rope.GetData().IsSleeping)
				{
					rope.WakeUp();
				}
			}
		}
	}
	[HarmonyPatch(typeof(RopeSyncer), "ShouldSendData")]
	public static class RopeSyncer_ShouldSendData_Patch
	{
		private static bool Prefix(RopeSyncer __instance, ref bool __result)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			Rope val = (Rope)AccessTools.Field(typeof(RopeSyncer), "rope").GetValue(__instance);
			if ((Object)(object)val == (Object)null)
			{
				return true;
			}
			RopeData data = val.GetData();
			if (!data.IsSleeping)
			{
				return true;
			}
			float unscaledTime = Time.unscaledTime;
			if (unscaledTime >= data.NextSleepSyncTime)
			{
				data.NextSleepSyncTime = unscaledTime + 1f;
				__result = true;
			}
			else
			{
				__result = false;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Rope), "AttachToAnchor_Rpc")]
	public static class Rope_AttachToAnchor_Rpc_Patch
	{
		private static void Postfix(Rope __instance)
		{
			if (PhotonNetwork.IsMasterClient && Plugin.CfgRopeOptimizerExperimental)
			{
				RopeData data = __instance.GetData();
				data.SleepCountdown = 10f;
			}
		}
	}
	[HarmonyPatch(typeof(Rope), "Awake")]
	public static class Rope_Awake_Patch
	{
		private static void Postfix(Rope __instance)
		{
			if (Plugin.CfgRopeOptimizerExperimental)
			{
				Rope_SleepRPCs rope_SleepRPCs = ((Component)__instance).gameObject.AddComponent<Rope_SleepRPCs>();
				rope_SleepRPCs.rope = __instance;
			}
		}
	}
	public class RopeData
	{
		public bool IsSleeping;

		public float SleepCountdown = -1f;

		public bool WasBeingClimbed;

		public float NextSleepSyncTime;
	}
	public static class Rope_ExtendedData
	{
		private static readonly ConditionalWeakTable<Rope, RopeData> AllRopeData = new ConditionalWeakTable<Rope, RopeData>();

		public const float SleepDelay = 10f;

		public static RopeData GetData(this Rope rope)
		{
			return AllRopeData.GetValue(rope, (Rope _) => new RopeData());
		}

		public static void GoToSleep(this Rope rope)
		{
			RopeData data = rope.GetData();
			if (!data.IsSleeping)
			{
				data.IsSleeping = true;
				data.SleepCountdown = -1f;
				data.WasBeingClimbed = false;
				data.NextSleepSyncTime = Time.unscaledTime + 1f;
				((Component)rope).GetComponent<PhotonView>().RPC("EnterSleepState_RPC", (RpcTarget)0, Array.Empty<object>());
			}
		}

		public static void WakeUp(this Rope rope)
		{
			RopeData data = rope.GetData();
			if (data.IsSleeping)
			{
				data.IsSleeping = false;
				data.SleepCountdown = 10f;
				data.WasBeingClimbed = false;
				data.NextSleepSyncTime = 0f;
				((Component)rope).GetComponent<PhotonView>().RPC("ExitSleepState_RPC", (RpcTarget)0, Array.Empty<object>());
			}
		}

		public static void UpdateSleepLogic(Rope rope)
		{
			RopeData data = rope.GetData();
			if (data.IsSleeping)
			{
				return;
			}
			bool flag = rope.charactersClimbing.Count > 0;
			if (data.WasBeingClimbed && !flag)
			{
				data.SleepCountdown = 10f;
			}
			data.WasBeingClimbed = flag;
			if (data.SleepCountdown > 0f && !flag)
			{
				data.SleepCountdown -= Time.deltaTime;
				if (data.SleepCountdown <= 0f)
				{
					rope.GoToSleep();
					data.SleepCountdown = -1f;
				}
			}
		}
	}
	[HarmonyPatch(typeof(Rope), "FixedUpdate")]
	public static class Rope_FixedUpdate_Patch
	{
		private static bool Prefix(Rope __instance)
		{
			RopeData data = __instance.GetData();
			return !data.IsSleeping;
		}
	}
	public class Rope_SleepRPCs : MonoBehaviourPun
	{
		public Rope rope;

		[PunRPC]
		private void EnterSleepState_RPC()
		{
			if (!Plugin.CfgRopeOptimizerExperimental)
			{
				return;
			}
			RopeData data = rope.GetData();
			data.IsSleeping = true;
			data.SleepCountdown = -1f;
			data.WasBeingClimbed = false;
			List<Transform> list = (List<Transform>)AccessTools.Field(typeof(Rope), "simulationSegments").GetValue(rope);
			foreach (Transform item in list)
			{
				Rigidbody component = ((Component)item).GetComponent<Rigidbody>();
				if ((Object)(object)component != (Object)null)
				{
					component.isKinematic = true;
				}
			}
			Debug.Log((object)$"Rope {((MonoBehaviourPun)rope).photonView.ViewID} is now sleeping.");
		}

		[PunRPC]
		private void ExitSleepState_RPC()
		{
			if (!Plugin.CfgRopeOptimizerExperimental)
			{
				return;
			}
			RopeData data = rope.GetData();
			data.IsSleeping = false;
			data.SleepCountdown = 10f;
			data.WasBeingClimbed = false;
			List<Transform> list = (List<Transform>)AccessTools.Field(typeof(Rope), "simulationSegments").GetValue(rope);
			foreach (Transform item in list)
			{
				Rigidbody component = ((Component)item).GetComponent<Rigidbody>();
				if ((Object)(object)component != (Object)null)
				{
					component.isKinematic = false;
					component.WakeUp();
				}
			}
			Debug.Log((object)$"Rope {((MonoBehaviourPun)rope).photonView.ViewID} has woken up.");
		}
	}
	[HarmonyPatch(typeof(Rope), "Update")]
	public static class Rope_Update_Patch
	{
		private static void Postfix(Rope __instance)
		{
			if (PhotonNetwork.IsMasterClient && Plugin.CfgRopeOptimizerExperimental)
			{
				Rope_ExtendedData.UpdateSleepLogic(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(RunManager), "Start")]
	public static class RunManager_Start_Patch
	{
		private static void Postfix()
		{
			if (Plugin.CfgRopeOptimizerExperimental)
			{
				Plugin.Log.LogInfo((object)"Experimental Rope Optimizer enabled! Enjoy the performance boost. Looking forward to your feedback!");
				RopeSleepManager.Initialize();
			}
		}
	}
	[HarmonyPatch(typeof(RunManager), "StartRun")]
	public class RunManagerStartRunPatch
	{
		[CompilerGenerated]
		private sealed class <FetchCacheAndSpawnInitial>d__1 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public int mapId;

			private Task<List<ServerStructureDto>> <task>5__2;

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

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

			[DebuggerHidden]
			public <FetchCacheAndSpawnInitial>d__1(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<task>5__2 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<task>5__2 = RemoteApi.FetchStructuresAsync(mapId);
					break;
				case 1:
					<>1__state = -1;
					break;
				}
				if (!<task>5__2.IsCompleted)
				{
					<>2__current = null;
					<>1__state = 1;
					return true;
				}
				if (<task>5__2.IsFaulted)
				{
					Plugin.Log.LogError((object)$"Failed to fetch remote structures: {<task>5__2.Exception}");
				}
				else
				{
					SaveManager.CacheRemoteStructures(<task>5__2.Result);
				}
				Plugin.Log.LogInfo((object)"Initial caching complete. Spawning structures for segment 0.");
				SaveManager.SpawnStructuresForSegment(0);
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private static void Postfix(RunManager __instance)
		{
			PeakStrandingSyncManager peakStrandingSyncManager = ((Component)__instance).GetComponent<PeakStrandingSyncManager>();
			if ((Object)(object)peakStrandingSyncManager == (Object)null)
			{
				peakStrandingSyncManager = ((Component)__instance).gameObject.AddComponent<PeakStrandingSyncManager>();
			}
			peakStrandingSyncManager.ResetRunLikes();
			if (!PhotonNetwork.IsMasterClient)
			{
				Plugin.Log.LogInfo((object)"New run started as a CLIENT, structures will be synced by the host.");
				return;
			}
			Plugin.Log.LogInfo((object)"New run started as a HOST. Caching structures.");
			SaveManager.ClearCache();
			if (Plugin.CfgLocalLoadStructures)
			{
				Plugin.Log.LogInfo((object)"Caching local structures from save.");
				SaveManager.CacheLocalStructures();
			}
			else
			{
				Plugin.Log.LogInfo((object)"Caching local structures is disabled, skipping.");
			}
			if (Plugin.CfgRemoteLoadStructures && DataHelper.GetCurrentSceneName() != "Airport")
			{
				Plugin.Log.LogInfo((object)"Fetching and caching remote structures from server.");
				((MonoBehaviour)__instance).StartCoroutine(FetchCacheAndSpawnInitial(DataHelper.GetCurrentLevelIndex()));
			}
			else
			{
				Plugin.Log.LogInfo((object)"Remote structures disabled or in Airport. Spawning initial local structures.");
				SaveManager.SpawnStructuresForSegment(0);
			}
		}

		[IteratorStateMachine(typeof(<FetchCacheAndSpawnInitial>d__1))]
		private static IEnumerator FetchCacheAndSpawnInitial(int mapId)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <FetchCacheAndSpawnInitial>d__1(0)
			{
				mapId = mapId
			};
		}
	}
}
namespace PeakStranding.Online
{
	internal class ApiException : HttpRequestException
	{
		public new HttpStatusCode StatusCode { get; }

		public ApiException(HttpStatusCode statusCode)
			: base($"Request failed with status code {statusCode}")
		{
			StatusCode = statusCode;
		}
	}
	internal static class RemoteApi
	{
		private static string _cachedSteamAuthTicket = string.Empty;

		private static readonly HttpClient http = new HttpClient
		{
			Timeout = TimeSpan.FromSeconds(10.0)
		};

		private static readonly string DefaultBaseUrl = "https://peakstranding.burning.homes/api/v1";

		internal static string StructuresUrl => GetBaseUrl() + "/structures";

		internal static string GetBaseUrl()
		{
			if (!string.IsNullOrEmpty(Plugin.CfgRemoteApiUrl))
			{
				return Plugin.CfgRemoteApiUrl.TrimEnd('/');
			}
			return DefaultBaseUrl;
		}

		internal static string StructureUrl(ulong id)
		{
			return $"{GetBaseUrl()}/structures/{id}";
		}

		private static string GetAuthTicket(bool forceRefresh = false)
		{
			if (forceRefresh || string.IsNullOrEmpty(_cachedSteamAuthTicket))
			{
				_cachedSteamAuthTicket = SteamAuthTicketService.GetSteamAuthTicket().Item1;
			}
			return _cachedSteamAuthTicket;
		}

		private static void InvalidateAuthTicket()
		{
			_cachedSteamAuthTicket = string.Empty;
		}

		public static void Upload(int mapId, PlacedItemData item)
		{
			PlacedItemData item2 = item;
			Task.Run(async delegate
			{
				try
				{
					await ExecuteWithAuthRetry(async delegate(string authTicket)
					{
						string content = JsonConvert.SerializeObject((object)item2.ToServerDto(SteamUser.GetSteamID().m_SteamID, SteamFriends.GetPersonaName(), mapId));
						Plugin.Log.LogInfo((object)"Uploading item to remote");
						using HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, StructuresUrl)
						{
							Content = new StringContent(content, Encoding.UTF8, "application/json")
						};
						req.Headers.Add("X-Steam-Auth", authTicket);
						HttpResponseMessage httpResponseMessage = await http.SendAsync(req).ConfigureAwait(continueOnCapturedContext: false);
						if (!httpResponseMessage.IsSuccessStatusCode)
						{
							throw new ApiException(httpResponseMessage.StatusCode);
						}
						return true;
					});
				}
				catch (Exception ex)
				{
					Plugin.Log.LogInfo((object)("Remote upload failed: " + ex.Message));
				}
				return Task.CompletedTask;
			});
		}

		public static async Task<List<ServerStructureDto>> FetchStructuresAsync(int mapId)
		{
			return await ExecuteWithAuthRetry(async delegate(string authTicket)
			{
				string text = Uri.EscapeDataString(DataHelper.GetCurrentSceneName());
				StringBuilder stringBuilder = new StringBuilder($"{StructuresUrl}?scene={text}&map_id={mapId}&limit={Plugin.CfgRemoteStructuresLimit}");
				List<string> excludedPrefabs = DataHelper.GetExcludedPrefabs();
				if (excludedPrefabs.Count > 0)
				{
					string text2 = Uri.EscapeDataString(string.Join(",", excludedPrefabs));
					stringBuilder.Append("&exclude_prefabs=" + text2);
				}
				string text3 = stringBuilder.ToString();
				Plugin.Log.LogInfo((object)("Fetching structures from remote: " + text3));
				using HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Get, text3);
				req.Headers.Add("X-Steam-Auth", authTicket);
				using HttpResponseMessage resp = await http.SendAsync(req).ConfigureAwait(continueOnCapturedContext: false);
				if (!resp.IsSuccessStatusCode)
				{
					throw new ApiException(resp.StatusCode);
				}
				List<ServerStructureDto> list = JsonConvert.DeserializeObject<List<ServerStructureDto>>(await resp.Content.ReadAsStringAsync().ConfigureAwait(continueOnCapturedContext: false)) ?? new List<ServerStructureDto>();
				HashSet<ulong> hashSet = new HashSet<ulong>();
				foreach (ServerStructureDto item in list)
				{
					hashSet.Add(item.user_id);
				}
				Plugin.Log.LogInfo((object)$"Received {list.Count} online structures from {hashSet.Count} users for map_id {mapId}");
				return list;
			});
		}

		public static async Task<bool> LikeStructureAsync(ulong id)
		{
			return await LikeStructureAsync(id, 1).ConfigureAwait(continueOnCapturedContext: false);
		}

		public static async Task<bool> LikeStructureAsync(ulong id, int count)
		{
			if (count <= 0)
			{
				return true;
			}
			return await ExecuteWithAuthRetry(async delegate(string authTicket)
			{
				string requestUri = StructureUrl(id) + "/like";
				string content = JsonConvert.SerializeObject((object)new { count });
				using HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, requestUri)
				{
					Content = new StringContent(content, Encoding.UTF8, "application/json")
				};
				req.Headers.Add("X-Steam-Auth", authTicket);
				using HttpResponseMessage httpResponseMessage = await http.SendAsync(req).ConfigureAwait(continueOnCapturedContext: false);
				if (!httpResponseMessage.IsSuccessStatusCode)
				{
					throw new ApiException(httpResponseMessage.StatusCode);
				}
				return true;
			});
		}

		private static async Task<T> ExecuteWithAuthRetry<T>(Func<string, Task<T>> action, int maxRetries = 1)
		{
			int attempt = 0;
			bool shouldRetry;
			Exception ex2;
			do
			{
				shouldRetry = false;
				try
				{
					string authTicket = GetAuthTicket(attempt > 0);
					return await action(authTicket).ConfigureAwait(continueOnCapturedContext: false);
				}
				catch (ApiException ex)
				{
					ex2 = ex;
					if (ex.StatusCode == HttpStatusCode.TooManyRequests)
					{
						Plugin.Log.LogWarning((object)"Request failed due to rate limiting. No retry will be attempted.");
						shouldRetry = false;
					}
					else if (attempt < maxRetries)
					{
						Plugin.Log.LogInfo((object)$"Request failed with HTTP status {ex.StatusCode}, refreshing auth ticket and retrying...");
						InvalidateAuthTicket();
						shouldRetry = true;
						attempt++;
					}
				}
				catch (HttpRequestException ex3)
				{
					ex2 = ex3;
					if (attempt < maxRetries)
					{
						Plugin.Log.LogInfo((object)("Request failed with " + ex3.Message + ", refreshing auth ticket and retrying..."));
						InvalidateAuthTicket();
						shouldRetry = true;
						attempt++;
					}
				}
				catch (Exception ex4)
				{
					ex2 = ex4;
					if (attempt < maxRetries)
					{
						shouldRetry = true;
						attempt++;
					}
				}
			}
			while (shouldRetry);
			Plugin.Log.LogInfo((object)$"Request failed after {attempt} attempts: {ex2?.Message}");
			if (ex2 != null)
			{
				throw ex2;
			}
			return default(T);
		}
	}
	public class LikeBuffer : MonoBehaviour
	{
		private struct Entry
		{
			public int Count;

			public float LastLikeAt;
		}

		private static LikeBuffer? _instance;

		private readonly Dictionary<ulong, Entry> _buffer = new Dictionary<ulong, Entry>();

		private readonly Queue<(ulong id, int count)> _readyToSend = new Queue<(ulong, int)>();

		private float _lastSentAt = -999f;

		private const float InactivitySeconds = 2f;

		private const float GlobalCooldown = 2f;

		private static readonly List<ulong> s_tempIds = new List<ulong>(16);

		public static void Ensure()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			if (!((Object)(object)_instance != (Object)null))
			{
				GameObject val = new GameObject("PeakStranding_LikeBuffer");
				_instance = val.AddComponent<LikeBuffer>();
				Object.DontDestroyOnLoad((Object)(object)val);
			}
		}

		public static void Enqueue(ulong structureId)
		{
			Ensure();
			_instance.EnqueueInternal(structureId);
		}

		private void EnqueueInternal(ulong id)
		{
			if (_buffer.TryGetValue(id, out var value))
			{
				value.Count++;
				value.LastLikeAt = Time.realtimeSinceStartup;
				_buffer[id] = value;
			}
			else
			{
				_buffer[id] = new Entry
				{
					Count = 1,
					LastLikeAt = Time.realtimeSinceStartup
				};
			}
		}

		private void Update()
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (_buffer.Count > 0)
			{
				s_tempIds.Clear();
				foreach (KeyValuePair<ulong, Entry> item3 in _buffer)
				{
					if (realtimeSinceStartup - item3.Value.LastLikeAt >= 2f)
					{
						s_tempIds.Add(item3.Key);
					}
				}
				foreach (ulong s_tempId in s_tempIds)
				{
					Entry entry = _buffer[s_tempId];
					_buffer.Remove(s_tempId);
					if (entry.Count > 0)
					{
						_readyToSend.Enqueue((s_tempId, entry.Count));
					}
				}
			}
			if (_readyToSend.Count > 0 && realtimeSinceStartup - _lastSentAt >= 2f)
			{
				(ulong id, int count) tuple = _readyToSend.Dequeue();
				ulong item = tuple.id;
				int item2 = tuple.count;
				_lastSentAt = realtimeSinceStartup;
				SendAsync(item, item2);
			}
		}

		private async Task SendAsync(ulong id, int count)
		{
			try
			{
				await RemoteApi.LikeStructureAsync(id, count).ConfigureAwait(continueOnCapturedContext: false);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogInfo((object)$"Batched like request failed for {id} x{count}: {ex.Message}");
			}
		}
	}
}
namespace PeakStranding.Data
{
	public static class DataHelper
	{
		public static BiDictionary prefabMapping = new BiDictionary
		{
			{ "0_Items/BounceShroomSpawn", "bounceshroom" },
			{ "0_Items/ClimbingSpikeHammered", "piton" },
			{ "0_Items/ShelfShroomSpawn", "shelfshroom" },
			{ "Flag_planted_seagull", "flagseagull" },
			{ "Flag_planted_turtle", "flagturtle" },
			{ "PeakStranding/JungleVine", "chainshooter" },
			{ "PeakStranding/MagicBeanVine", "magicbean" },
			{ "PeakStranding/RopeShooter", "ropeshooter" },
			{ "PeakStranding/RopeSpool", "ropespool" },
			{ "PortableStovetop_Placed", "stove" },
			{ "ScoutCannon_Placed", "scoutcannon" }
		};

		public static string GetCurrentSceneName()
		{
			//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)
			Scene activeScene = SceneManager.GetActiveScene();
			return ((Scene)(ref activeScene)).name;
		}

		public static int GetCurrentMapSegment()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected I4, but got Unknown
			if ((Object)(object)Singleton<MapHandler>.Instance == (Object)null)
			{
				return -1;
			}
			return (int)Singleton<MapHandler>.Instance.GetCurrentSegment();
		}

		public static int GetCurrentLevelIndex()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			return GameHandler.GetService<NextLevelService>().Data.Value.CurrentLevelIndex;
		}

		public static bool IsPrefabAllowed(string prefabName)
		{
			if (string.IsNullOrWhiteSpace(prefabName))
			{
				return false;
			}
			prefabMapping.TryGetByFirst(prefabName, out string second);
			if (string.IsNullOrWhiteSpace(second))
			{
				return false;
			}
			if (string.IsNullOrWhiteSpace(Plugin.CfgStructureAllowList))
			{
				return true;
			}
			return Plugin.CfgStructureAllowList.ToLower().Contains(second.ToLower());
		}

		public static List<string> GetExcludedPrefabs()
		{
			List<string> list = new List<string>();
			if (string.IsNullOrWhiteSpace(Plugin.CfgStructureAllowList))
			{
				return list;
			}
			HashSet<string> hashSet = new HashSet<string>(Plugin.CfgStructureAllowList.ToLower().Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries));
			foreach (KeyValuePair<string, string> item in prefabMapping)
			{
				string key = item.Key;
				string value = item.Value;
				if (!hashSet.Contains(value.ToLower()))
				{
					list.Add(key);
				}
			}
			return list;
		}
	}
	[Serializable]
	public class PlacedItemData
	{
		public string PrefabName = string.Empty;

		public Vector3 Position;

		public Quaternion Rotation;

		public Vector3 RopeStart;

		public Vector3 RopeEnd;

		public float RopeLength;

		public Vector3 RopeFlyingRotation;

		public Quaternion RopeAnchorRotation;

		public bool RopeAntiGrav;

		public string Scene = string.Empty;

		public int MapSegment;

		public void AddCurrentRunContext()
		{
			Scene = DataHelper.GetCurrentSceneName();
			MapSegment = DataHelper.GetCurrentMapSegment();
		}
	}
	public class RestoredItem : MonoBehaviour
	{
	}
	public class RestoredItemCredits : MonoBehaviour
	{
		private Camera _mainCam;

		public string displayText = "";

		private const float kDistanceSqr = 100f;

		private const int kLazyInterval = 30;

		private bool _isNear;

		private int _nextCheckFrame;

		private void Awake()
		{
			_mainCam = Camera.main;
			if ((Object)(object)_mainCam == (Object)null)
			{
				Plugin.Log.LogError((object)"Main camera not found.");
			}
			_nextCheckFrame = 30;
		}

		private void Update()
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_mainCam == (Object)null || string.IsNullOrEmpty(displayText) || (!_isNear && Time.frameCount < _nextCheckFrame))
			{
				return;
			}
			Vector3 val = ((Component)_mainCam).transform.position - ((Component)this).transform.position;
			bool flag = ((Vector3)(ref val)).sqrMagnitude <= 100f;
			if (_isNear != flag)
			{
				_isNear = flag;
				if (!_isNear)
				{
					_nextCheckFrame = Time.frameCount + 30;
				}
			}
			else if (!_isNear)
			{
				_nextCheckFrame = Time.frameCount + 30;
			}
		}

		private void OnGUI()
		{
			//IL_0030: 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_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: 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_0069: Expected O, but got Unknown
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: 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_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			if (_isNear && !((Object)(object)_mainCam == (Object)null) && !string.IsNullOrEmpty(displayText))
			{
				Vector3 val = _mainCam.WorldToScreenPoint(((Component)this).transform.position);
				if (!(val.z < 0f))
				{
					val.y = (float)Screen.height - val.y;
					GUIContent val2 = new GUIContent(displayText);
					GUIStyle label = GUI.skin.label;
					label.fontSize = 18;
					label.alignment = (TextAnchor)4;
					label.wordWrap = true;
					label.normal.textColor = Color.white;
					float num = 200f;
					float num2 = label.CalcHeight(val2, num);
					float num3 = 2f;
					Rect val3 = default(Rect);
					((Rect)(ref val3))..ctor(val.x - num * 0.5f, val.y - num2 * 0.5f - num3, num, num2 + num3 * 2f);
					GUI.color = new Color(0f, 0f, 0f, 0.15f);
					GUI.DrawTexture(val3, (Texture)(object)Texture2D.whiteTexture);
					GUI.color = Color.white;
					GUI.Label(val3, val2, label);
				}
			}
		}
	}
	[Serializable]
	public class ServerStructureDto
	{
		public ulong id;

		public ulong user_id;

		public string username = string.Empty;

		public int map_id;

		public string scene = string.Empty;

		public int segment;

		public string prefab = string.Empty;

		public float pos_x;

		public float pos_y;

		public float pos_z;

		public float rot_x;

		public float rot_y;

		public float rot_z;

		public float rot_w;

		public float rope_start_x;

		public float rope_start_y;

		public float rope_start_z;

		public float rope_end_x;

		public float rope_end_y;

		public float rope_end_z;

		public float rope_length;

		public float rope_flying_rotation_x;

		public float rope_flying_rotation_y;

		public float rope_flying_rotation_z;

		public float rope_anchor_rotation_x;

		public float rope_anchor_rotation_y;

		public float rope_anchor_rotation_z;

		public float rope_anchor_rotation_w;

		public bool antigrav;

		public int likes;
	}
	public static class ServerStructureConverters
	{
		public static ServerStructureDto ToServerDto(this PlacedItemData item, ulong userSteamId, string username, int mapId)
		{
			return new ServerStructureDto
			{
				user_id = userSteamId,
				username = username,
				map_id = mapId,
				scene = item.Scene,
				segment = item.MapSegment,
				prefab = item.PrefabName,
				pos_x = item.Position.x,
				pos_y = item.Position.y,
				pos_z = item.Position.z,
				rot_x = item.Rotation.x,
				rot_y = item.Rotation.y,
				rot_z = item.Rotation.z,
				rot_w = item.Rotation.w,
				rope_start_x = item.RopeStart.x,
				rope_start_y = item.RopeStart.y,
				rope_start_z = item.RopeStart.z,
				rope_end_x = item.RopeEnd.x,
				rope_end_y = item.RopeEnd.y,
				rope_end_z = item.RopeEnd.z,
				rope_length = item.RopeLength,
				rope_flying_rotation_x = item.RopeFlyingRotation.x,
				rope_flying_rotation_y = item.RopeFlyingRotation.y,
				rope_flying_rotation_z = item.RopeFlyingRotation.z,
				rope_anchor_rotation_x = item.RopeAnchorRotation.x,
				rope_anchor_rotation_y = item.RopeAnchorRotation.y,
				rope_anchor_rotation_z = item.RopeAnchorRotation.z,
				rope_anchor_rotation_w = item.RopeAnchorRotation.w,
				antigrav = item.RopeAntiGrav
			};
		}

		public static PlacedItemData ToPlacedItemData(this ServerStructureDto s)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: 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_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			return new PlacedItemData
			{
				PrefabName = s.prefab,
				Position = new Vector3(s.pos_x, s.pos_y, s.pos_z),
				Rotation = new Quaternion(s.rot_x, s.rot_y, s.rot_z, s.rot_w),
				RopeStart = new Vector3(s.rope_start_x, s.rope_start_y, s.rope_start_z),
				RopeEnd = new Vector3(s.rope_end_x, s.rope_end_y, s.rope_end_z),
				RopeLength = s.rope_length,
				RopeFlyingRotation = new Vector3(s.rope_flying_rotation_x, s.rope_flying_rotation_y, s.rope_flying_rotation_z),
				RopeAnchorRotation = new Quaternion(s.rope_anchor_rotation_x, s.rope_anchor_rotation_y, s.rope_anchor_rotation_z, s.rope_anchor_rotation_w),
				RopeAntiGrav = s.antigrav,
				Scene = s.scene,
				MapSegment = s.segment
			};
		}

		public static List<PlacedItemData> ToPlacedItemList(this IEnumerable<ServerStructureDto> rows)
		{
			return rows.Select((ServerStructureDto dto) => dto.ToPlacedItemData()).ToList();
		}
	}
	[Serializable]
	public class StructureSyncData
	{
		public int ViewID { get; set; }

		public string? Username { get; set; }

		public int Likes { get; set; }

		public ulong ServerId { get; set; }

		public ulong UserId { get; set; }

		public bool LikeEnabled { get; set; }
	}
}
namespace PeakStranding.Components
{
	public class DeletableGroup : MonoBehaviourPun
	{
		[SerializeField]
		private List<PhotonView> _pvs = new List<PhotonView>();

		[SerializeField]
		private List<GameObject> _gos = new List<GameObject>();

		public void Add(PhotonView pv)
		{
			if (!((Object)(object)pv == (Object)null) && !_pvs.Contains(pv))
			{
				_pvs.Add(pv);
			}
		}

		public void Add(GameObject go)
		{
			if (!((Object)(object)go == (Object)null) && !_gos.Contains(go))
			{
				_gos.Add(go);
			}
		}

		[PunRPC]
		public void PS_Delete()
		{
			for (int num = _pvs.Count - 1; num >= 0; num--)
			{
				PhotonView val = _pvs[num];
				if ((Object)(object)val == (Object)null)
				{
					_pvs.RemoveAt(num);
				}
				else if ((Object)(object)val != (Object)null)
				{
					PhotonNetwork.Destroy(val);
				}
			}
			for (int num2 = _gos.Count - 1; num2 >= 0; num2--)
			{
				GameObject val2 = _gos[num2];
				if ((Object)(object)val2 == (Object)null)
				{
					_gos.RemoveAt(num2);
				}
				else
				{
					Object.Destroy((Object)(object)val2);
				}
			}
		}
	}
	public static class DeletionUtility
	{
		private static GameObject? TryGetRopeAnchorObject(Rope rope)
		{
			try
			{
				FieldInfo field = typeof(Rope).GetField("attachedToAnchor", BindingFlags.Instance | BindingFlags.NonPublic);
				if (field == null)
				{
					return null;
				}
				object? value = field.GetValue(rope);
				RopeAnchor val = (RopeAnchor)((value is RopeAnchor) ? value : null);
				return ((Object)(object)val != (Object)null) ? ((Component)val).gameObject : null;
			}
			catch
			{
				return null;
			}
		}

		public static void Delete(GameObject go)
		{
			if ((Object)(object)go == (Object)null)
			{
				return;
			}
			if (((Object)go).name.Contains("ChainShootable"))
			{
				Plugin.Log.LogInfo((object)"We are NOT deleting the Chain. Like NO. Main game bugs.");
				return;
			}
			DeletableGroup componentInParent = go.GetComponentInParent<DeletableGroup>();
			if ((Object)(object)componentInParent != (Object)null && (Object)(object)((MonoBehaviourPun)componentInParent).photonView != (Object)null)
			{
				if (PhotonNetwork.IsMasterClient)
				{
					((MonoBehaviourPun)componentInParent).photonView.RPC("PS_Delete", (RpcTarget)3, Array.Empty<object>());
				}
				else
				{
					Plugin.Log.LogInfo((object)"Remove requested by non-host; only host can remove globally.");
				}
				return;
			}
			if (PhotonNetwork.IsMasterClient)
			{
				RopeAnchorWithRope componentInChildren = go.GetComponentInChildren<RopeAnchorWithRope>();
				if ((Object)(object)componentInChildren != (Object)null)
				{
					if ((Object)(object)componentInChildren.rope != (Object)null && (Object)(object)((MonoBehaviourPun)componentInChildren.rope).photonView != (Object)null)
					{
						PhotonNetwork.Destroy(((MonoBehaviourPun)componentInChildren.rope).photonView);
					}
					PhotonView component = ((Component)componentInChildren).GetComponent<PhotonView>();
					if ((Object)(object)component != (Object)null)
					{
						PhotonNetwork.Destroy(component);
					}
					return;
				}
				RopeAnchor componentInChildren2 = go.GetComponentInChildren<RopeAnchor>();
				if ((Object)(object)componentInChildren2 != (Object)null)
				{
					Rope val = FindRopeForAnchor(componentInChildren2);
					if ((Object)(object)val != (Object)null && (Object)(object)((MonoBehaviourPun)val).photonView != (Object)null)
					{
						PhotonNetwork.Destroy(((MonoBehaviourPun)val).photonView);
					}
					PhotonView val2 = default(PhotonView);
					if (((Component)componentInChildren2).TryGetComponent<PhotonView>(ref val2))
					{
						PhotonNetwork.Destroy(val2);
					}
					return;
				}
				Rope componentInChildren3 = go.GetComponentInChildren<Rope>();
				if ((Object)(object)componentInChildren3 != (Object)null)
				{
					GameObject val3 = TryGetRopeAnchorObject(componentInChildren3);
					PhotonView val4 = default(PhotonView);
					if ((Object)(object)val3 != (Object)null && val3.TryGetComponent<PhotonView>(ref val4))
					{
						PhotonNetwork.Destroy(val4);
					}
					if ((Object)(object)((MonoBehaviourPun)componentInChildren3).photonView != (Object)null)
					{
						PhotonNetwork.Destroy(((MonoBehaviourPun)componentInChildren3).photonView);
					}
					return;
				}
			}
			PhotonView component2 = go.GetComponent<PhotonView>();
			if (PhotonNetwork.IsMasterClient)
			{
				if ((Object)(object)component2 != (Object)null)
				{
					PhotonNetwork.Destroy(component2);
				}
				else
				{
					Object.Destroy((Object)(object)go);
				}
			}
			else
			{
				Plugin.Log.LogInfo((object)"Remove requested by non-host; only host can remove globally.");
			}
		}

		private static Rope? FindRopeForAnchor(RopeAnchor anchor)
		{
			if ((Object)(object)anchor == (Object)null)
			{
				return null;
			}
			try
			{
				Rope[] array = Object.FindObjectsByType<Rope>((FindObjectsSortMode)0);
				Rope[] array2 = array;
				foreach (Rope val in array2)
				{
					GameObject val2 = TryGetRopeAnchorObject(val);
					if (!((Object)(object)val2 == (Object)null))
					{
						if ((Object)(object)val2 == (Object)(object)((Component)anchor).gameObject)
						{
							return val;
						}
						RopeAnchor component = val2.GetComponent<RopeAnchor>();
						if ((Object)(object)component == (Object)(object)anchor)
						{
							return val;
						}
					}
				}
			}
			catch
			{
			}
			return null;
		}
	}
	public class MagicBeanLink : MonoBehaviour
	{
		public MagicBeanVine? vine;

		public bool isRestoredBean;

		private void OnDestroy()
		{
			if (isRestoredBean && (Object)(object)vine != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)vine).gameObject);
				vine = null;
			}
		}
	}
	public class OverlayManager : MonoBehaviour
	{
		public struct RegisterInfo
		{
			public GameObject target;

			public string username;

			public int likes;

			public ulong id;

			public ulong user_id;

			public bool canLike;
		}

		public class Entry
		{
			public Transform t;

			public string username = string.Empty;

			public int likes;

			public ulong id;

			public ulong user_id;

			public bool canLike = true;

			public GameObject go;

			public List<Floater> floaters = new List<Floater>();

			public float likeTickT;

			public float removeS;

			public bool removing;
		}

		public struct Floater
		{
			public float t;

			public float duration;

			public float xJitter;

			public float scale;
		}

		private static OverlayManager? _instance;

		public const int MaxOverlays = 10;

		public const float MaxDistance = 10f;

		private const float MaxDistanceSqr = 100f;

		private const int ThrottleFrames = 10;

		public static bool DebugShowAll = false;

		private readonly List<Entry> _entries = new List<Entry>();

		private readonly List<Entry> _visible = new List<Entry>();

		private Camera _cam;

		private int _nextCullFrame;

		private GUIStyle _labelStyle;

		private Texture2D _bgTex = Texture2D.whiteTexture;

		public static float UiScale = 1.2f;

		public static int MaxNameChars = 20;

		public static KeyCode LikeKey = (KeyCode)108;

		public static KeyCode RemoveKey = (KeyCode)127;

		public static float RemoveHoldSeconds = 0.6f;

		public static bool ClientsCanLike = true;

		public static bool ClientsCanDelete = true;

		private Entry? _focused;

		private float _removeHeldTime;

		private Entry? _lastFocused;

		public static OverlayManager Instance
		{
			get
			{
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_0018: Expected O, but got Unknown
				if ((Object)(object)_instance == (Object)null)
				{
					GameObject val = new GameObject("PeakStranding_OverlayManager");
					_instance = val.AddComponent<OverlayManager>();
					Object.DontDestroyOnLoad((Object)(object)val);
				}
				return _instance;
			}
		}

		private static GameObject? TryGetRopeAnchorObject(Rope rope)
		{
			try
			{
				FieldInfo field = typeof(Rope).GetField("attachedToAnchor", BindingFlags.Instance | BindingFlags.NonPublic);
				if (field == null)
				{
					return null;
				}
				object? value = field.GetValue(rope);
				RopeAnchor val = (RopeAnchor)((value is RopeAnchor) ? value : null);
				return ((Object)(object)val != (Object)null) ? ((Component)val).gameObject : null;
			}
			catch
			{
				return null;
			}
		}

		private void Awake()
		{
			if ((Object)(object)_instance != (Object)null && (Object)(object)_instance != (Object)(object)this)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
				return;
			}
			_instance = this;
			_cam = Camera.main;
			_bgTex = Texture2D.whiteTexture;
		}

		private void EnsureGui()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			if (_labelStyle == null)
			{
				GUIStyle val = new GUIStyle(GUI.skin.label)
				{
					fontSize = Mathf.RoundToInt(14f * UiScale),
					alignment = (TextAnchor)4,
					wordWrap = false
				};
				val.normal.textColor = Color.white;
				_labelStyle = val;
			}
		}

		public static void Register(RegisterInfo info)
		{
			if ((Object)(object)info.target == (Object)null)
			{
				return;
			}
			OverlayManager instance = Instance;
			DeletableGroup componentInParent = info.target.GetComponentInParent<DeletableGroup>();
			if ((Object)(object)componentInParent != (Object)null)
			{
				GameObject root2 = ((Component)componentInParent).gameObject;
				if (!instance._entries.Any((Entry e) => (Object)(object)e.go == (Object)(object)root2))
				{
					instance._entries.Add(new Entry
					{
						go = root2,
						t = root2.transform,
						username = (info.username ?? ""),
						likes = Mathf.Max(0, info.likes),
						id = info.id,
						user_id = info.user_id,
						canLike = info.canLike
					});
				}
				return;
			}
			Rope component = info.target.GetComponent<Rope>();
			if ((Object)(object)component != (Object)null)
			{
				GameObject val = TryGetRopeAnchorObject(component);
				if ((Object)(object)val != (Object)null)
				{
					DeletableGroup component2 = val.GetComponent<DeletableGroup>();
					if ((Object)(object)component2 != (Object)null)
					{
						GameObject root = ((Component)component2).gameObject;
						if (!instance._entries.Any((Entry e) => (Object)(object)e.go == (Object)(object)root))
						{
							instance._entries.Add(new Entry
							{
								go = root,
								t = root.transform,
								username = (info.username ?? ""),
								likes = Mathf.Max(0, info.likes),
								id = info.id,
								user_id = info.user_id,
								canLike = info.canLike
							});
						}
						return;
					}
				}
			}
			if (!instance._entries.Any((Entry e) => (Object)(object)e.go == (Object)(object)info.target))
			{
				instance._entries.Add(new Entry
				{
					go = info.target,
					t = info.target.transform,
					username = (info.username ?? ""),
					likes = Mathf.Max(0, info.likes),
					id = info.id,
					user_id = info.user_id,
					canLike = info.canLike
				});
			}
		}

		public static void Unregister(GameObject target)
		{
			GameObject target2 = target;
			if (!((Object)(object)target2 == (Object)null) && !((Object)(object)_instance == (Object)null))
			{
				_instance._entries.RemoveAll((Entry e) => (Object)(object)e.go == (Object)(object)target2);
				_instance._visible.RemoveAll((Entry e) => (Object)(object)e.go == (Object)(object)target2);
			}
		}

		public List<Entry> GetAllEntriesForSync()
		{
			return new List<Entry>(_entries);
		}

		public void ApplyLikeBroadcast(GameObject target, int newLikeCount, bool canLike)
		{
			GameObject target2 = target;
			Entry entry = _entries.FirstOrDefault((Entry e) => (Object)(object)e.go == (Object)(object)target2);
			if (entry != null)
			{
				entry.likes = newLikeCount;
				entry.canLike = canLike;
				if (entry.floaters == null)
				{
					entry.floaters = new List<Floater>(4);
				}
				Floater floater = default(Floater);
				floater.t = 0f;
				floater.duration = 0.5f;
				floater.xJitter = Random.Range(-8f * UiScale, 8f * UiScale);
				floater.scale = Random.Range(0.9f, 1.1f);
				Floater item = floater;
				entry.floaters.Add(item);
				entry.likeTickT = 1f;
			}
		}

		public Entry FindEntry(GameObject target)
		{
			GameObject target2 = target;
			return _entries.FirstOrDefault((Entry e) => (Object)(object)e.go == (Object)(object)target2);
		}

		private void Update()
		{
			if ((Object)(object)_cam == (Object)null)
			{
				_cam = Camera.main;
			}
			if ((Object)(object)_cam == (Object)null)
			{
				return;
			}
			for (int num = _entries.Count - 1; num >= 0; num--)
			{
				if ((Object)(object)_entries[num].go == (Object)null)
				{
					_entries.RemoveAt(num);
				}
			}
			for (int i = 0; i < _entries.Count; i++)
			{
				Entry entry = _entries[i];
				if ((Object)(object)entry.go == (Object)null)
				{
					continue;
				}
				DeletableGroup componentInParent = entry.go.GetComponentInParent<DeletableGroup>();
				if ((Object)(object)componentInParent != (Object)null && (Object)(object)((Component)componentInParent).gameObject != (Object)(object)entry.go)
				{
					GameObject root = ((Component)componentInParent).gameObject;
					if (_entries.Any((Entry x) => (Object)(object)x.go == (Object)(object)root))
					{
						_entries.RemoveAt(i);
						i--;
					}
					else
					{
						entry.go = root;
						entry.t = root.transform;
						_entries[i] = entry;
					}
					continue;
				}
				Rope component = entry.go.GetComponent<Rope>();
				if (!((Object)(object)component != (Object)null))
				{
					continue;
				}
				GameObject val = TryGetRopeAnchorObject(component);
				if (!((Object)(object)val != (Object)null))
				{
					continue;
				}
				DeletableGroup component2 = val.GetComponent<DeletableGroup>();
				if ((Object)(object)component2 != (Object)null)
				{
					GameObject root2 = ((Component)component2).gameObject;
					if (_entries.Any((Entry x) => (Object)(object)x.go == (Object)(object)root2))
					{
						_entries.RemoveAt(i);
						i--;
					}
					else
					{
						entry.go = root2;
						entry.t = root2.transform;
						_entries[i] = entry;
					}
				}
			}
			if (Time.frameCount >= _nextCullFrame)
			{
				_nextCullFrame = Time.frameCount + 10;
				RebuildVisible();
			}
			UpdateFocus();
			HandleInput();
		}

		private void RebuildVisible()
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			_visible.Clear();
			if (_entries.Count == 0)
			{
				return;
			}
			if (DebugShowAll)
			{
				_visible.AddRange(_entries);
				return;
			}
			Vector3 camPos = ((Component)_cam).transform.position;
			IEnumerable<Entry> collection = from x in (from x in _entries.Select(delegate(Entry e)
					{
						//IL_0007: 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_0012: Unknown result type (might be due to invalid IL or missing references)
						//IL_0017: Unknown result type (might be due to invalid IL or missing references)
						Vector3 val = e.t.position - camPos;
						return (e, ((Vector3)(ref val)).sqrMagnitude);
					})
					where x.d2 <= 100f
					orderby x.d2
					select x).Take(10)
				select x.e;
			_visible.AddRange(collection);
		}

		private void UpdateFocus()
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: 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_0094: 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)
			//IL_009b: 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_00b8: 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_00c7: 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)
			_focused = null;
			if (_visible.Count == 0 || (Object)(object)_cam == (Object)null)
			{
				return;
			}
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor((float)Screen.width * 0.5f, (float)Screen.height * 0.5f);
			float num = float.MaxValue;
			Vector2 val3 = default(Vector2);
			foreach (Entry item in _visible)
			{
				if ((Object)(object)item.t == (Object)null)
				{
					continue;
				}
				Vector3 val2 = _cam.WorldToScreenPoint(item.t.position + Vector3.up * 0.6f);
				if (!(val2.z < 0f))
				{
					((Vector2)(ref val3))..ctor(val2.x, (float)Screen.height - val2.y);
					float num2 = Vector2.SqrMagnitude(val3 - val);
					if (num2 < num)
					{
						num = num2;
						_focused = item;
					}
				}
			}
			if (_focused == null)
			{
				_removeHeldTime = 0f;
			}
		}

		private void HandleInput()
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			if (_focused == null)
			{
				_removeHeldTime = 0f;
				return;
			}
			bool flag = _focused.canLike && (PhotonNetwork.IsMasterClient || ClientsCanLike);
			bool flag2 = PhotonNetwork.IsMasterClient || ClientsCanDelete;
			if (flag && Input.GetKeyDown(LikeKey))
			{
				TryLike(_focused);
			}
			if (flag2 && Input.GetKey(RemoveKey))
			{
				_removeHeldTime += Time.unscaledDeltaTime;
				if (_removeHeldTime >= RemoveHoldSeconds)
				{
					TryRemove(_focused);
					_removeHeldTime = 0f;
				}
			}
			else
			{
				_removeHeldTime = 0f;
			}
		}

		private void OnGUI()
		{
			//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_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_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0214: Expected O, but got Unknown
			//IL_023e: Unknown result type (might be due to invalid IL or missing references)
			//IL_024d: Expected O, but got Unknown
			//IL_028b: Unknown result type (might be due to invalid IL or missing references)
			//IL_029a: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0448: Unknown result type (might be due to invalid IL or missing references)
			//IL_0464: Unknown result type (might be due to invalid IL or missing references)
			//IL_0471: Unknown result type (might be due to invalid IL or missing references)
			//IL_0476: Unknown result type (might be due to invalid IL or missing references)
			//IL_0478: Unknown result type (might be due to invalid IL or missing references)
			//IL_047f: Unknown result type (might be due to invalid IL or missing references)
			//IL_048c: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c9: Expected O, but got Unknown
			//IL_04e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d5: Expected O, but got Unknown
			//IL_05d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_05da: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_05f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0611: Unknown result type (might be due to invalid IL or missing references)
			//IL_061b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0632: Unknown result type (might be due to invalid IL or missing references)
			//IL_0637: Unknown result type (might be due to invalid IL or missing references)
			//IL_0642: Unknown result type (might be due to invalid IL or missing references)
			//IL_0649: Unknown result type (might be due to invalid IL or missing references)
			//IL_0650: Unknown result type (might be due to invalid IL or missing references)
			//IL_065e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0573: Unknown result type (might be due to invalid IL or missing references)
			//IL_083c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0841: Unknown result type (might be due to invalid IL or missing references)
			//IL_0857: Unknown result type (might be due to invalid IL or missing references)
			//IL_085e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0864: Unknown result type (might be due to invalid IL or missing references)
			//IL_0870: Expected O, but got Unknown
			//IL_08e9: Unknown result type (might be due to