Decompiled source of PeakStranding v0.9.3

plugins/com.github.wafflecomposite.PeakStranding.dll

Decompiled a week 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 PeakStranding.UI;
using Photon.Pun;
using Photon.Realtime;
using Steamworks;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
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.3.0")]
[assembly: AssemblyInformationalVersion("0.9.3+352eb678edc38ccc798c2bae5303d3ba4654296b")]
[assembly: AssemblyProduct("com.github.wafflecomposite.PeakStranding")]
[assembly: AssemblyTitle("PeakStranding")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.9.3.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.3")]
	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> showStructureOverlayConfig;

		internal static ConfigEntry<bool> showToastsConfig;

		internal static ConfigEntry<bool> ropeOptimizerExperimentalConfig;

		internal static ConfigEntry<string> structureAllowListConfig;

		internal static ConfigEntry<bool> allowClientsLikeConfig;

		internal static ConfigEntry<bool> allowClientsDeleteConfig;

		internal static ConfigEntry<KeyCode> overlayLikeKeyConfig;

		internal static ConfigEntry<KeyCode> overlayRemoveKeyConfig;

		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 CfgShowStructureOverlay => showStructureOverlayConfig.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 bool CfgShowToasts => showToastsConfig.Value;

		public static string Name => "PeakStranding";

		public static string Version => "0.9.3";

		private void Awake()
		{
			//IL_020a: Unknown result type (might be due to invalid IL or missing references)
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_029e: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a4: Expected O, but got Unknown
			//IL_02da: Unknown result type (might be due to invalid IL or missing references)
			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");
			showStructureOverlayConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "Show_Structure_Overlay", true, "Whether to show the overlay for structures placed by other players in the world");
			showToastsConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "Show_Toasts", true, "Enable or disable toast notifications 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.");
			overlayLikeKeyConfig = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "Overlay_Like_Key", (KeyCode)108, "Key used to like structures from the overlay.");
			overlayRemoveKeyConfig = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "Overlay_Remove_Key", (KeyCode)127, "Key used to remove structures from the overlay.");
			OverlayManager.LikeShortcut = overlayLikeKeyConfig.Value;
			OverlayManager.RemoveShortcut = overlayRemoveKeyConfig.Value;
			overlayLikeKeyConfig.SettingChanged += delegate
			{
				//IL_0005: Unknown result type (might be due to invalid IL or missing references)
				//IL_000a: Unknown result type (might be due to invalid IL or missing references)
				OverlayManager.LikeShortcut = overlayLikeKeyConfig.Value;
			};
			overlayRemoveKeyConfig.SettingChanged += delegate
			{
				//IL_0005: Unknown result type (might be due to invalid IL or missing references)
				//IL_000a: Unknown result type (might be due to invalid IL or missing references)
				OverlayManager.RemoveShortcut = overlayRemoveKeyConfig.Value;
			};
			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!"));
			if ((Object)(object)ToastController.Instance == (Object)null)
			{
				new GameObject("PeakStranding UI Toasts").AddComponent<ToastController>();
			}
			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)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			if (!PhotonNetwork.IsMasterClient)
			{
				return;
			}
			if (items == null || items.Count == 0)
			{
				Plugin.Log.LogInfo((object)"No remote structures to cache.");
				ToastController.Instance.Toast("PeakStranding: no online items found for current map!?", Color.yellow, 5f, 3f);
				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));
			}
			ToastController.Instance.Toast($"PeakStranding: {items.Count} online items have been loaded!", Color.green, 5f, 3f);
		}

		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.UI
{
	public static class FontUtility
	{
		private static TMP_FontAsset _font;

		public static bool TryGetFont(out TMP_FontAsset font)
		{
			if ((Object)(object)_font == (Object)null && (Object)(object)GUIManager.instance != (Object)null && (Object)(object)GUIManager.instance.heroDayText != (Object)null && (Object)(object)((TMP_Text)GUIManager.instance.heroDayText).font != (Object)null)
			{
				_font = ((TMP_Text)GUIManager.instance.heroDayText).font;
			}
			font = _font;
			return (Object)(object)font != (Object)null;
		}
	}
	public class ToastController : MonoBehaviour
	{
		[CompilerGenerated]
		private sealed class <ClearToast>d__7 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public ToastController <>4__this;

			public float duration;

			public float fadeTime;

			private float <timeElapsed>5__2;

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

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

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

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

			private bool MoveNext()
			{
				//IL_004d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0057: Expected O, but got Unknown
				int num = <>1__state;
				ToastController toastController = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					if ((Object)(object)toastController.textMesh == (Object)null)
					{
						return false;
					}
					if (duration > 0f)
					{
						<>2__current = (object)new WaitForSeconds(duration);
						<>1__state = 1;
						return true;
					}
					goto IL_0067;
				case 1:
					<>1__state = -1;
					goto IL_0067;
				case 2:
					{
						<>1__state = -1;
						break;
					}
					IL_0067:
					<timeElapsed>5__2 = 0f;
					break;
				}
				if (<timeElapsed>5__2 < fadeTime)
				{
					<timeElapsed>5__2 += Time.deltaTime;
					float num2 = ((fadeTime <= 0f) ? 1f : Mathf.Clamp01(<timeElapsed>5__2 / fadeTime));
					((TMP_Text)toastController.textMesh).alpha = Mathf.Lerp(1f, 0f, num2);
					<>2__current = null;
					<>1__state = 2;
					return true;
				}
				((TMP_Text)toastController.textMesh).alpha = 0f;
				toastController.clearCoroutine = null;
				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();
			}
		}

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

			private object <>2__current;

			public ToastController <>4__this;

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

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

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

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

			private bool MoveNext()
			{
				int num = <>1__state;
				ToastController toastController = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					break;
				case 1:
					<>1__state = -1;
					break;
				}
				if (!FontUtility.TryGetFont(out TMP_FontAsset font))
				{
					<>2__current = null;
					<>1__state = 1;
					return true;
				}
				if ((Object)(object)toastController.textMesh != (Object)null)
				{
					((TMP_Text)toastController.textMesh).font = font;
				}
				toastController.fontCoroutine = null;
				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 static ToastController Instance;

		private TextMeshProUGUI textMesh;

		private Coroutine clearCoroutine;

		private Coroutine fontCoroutine;

		private void Awake()
		{
			Instance = this;
			Object.DontDestroyOnLoad((Object)(object)this);
		}

		private void Start()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			Plugin.Log.LogInfo((object)"Initializing Toast Controller...");
			Canvas val = new GameObject("canvas").AddComponent<Canvas>();
			((Component)val).transform.SetParent(((Component)this).transform);
			val.renderMode = (RenderMode)0;
			val.sortingOrder = 32767;
			((Component)val).gameObject.AddComponent<CanvasScaler>();
			GameObject val2 = new GameObject("text");
			val2.transform.SetParent(((Component)val).transform, false);
			RectTransform val3 = val2.AddComponent<RectTransform>();
			val3.sizeDelta = new Vector2(1000f, 100f);
			val3.anchorMin = new Vector2(1f, 1f);
			val3.anchorMax = new Vector2(1f, 1f);
			val3.pivot = new Vector2(1f, 1f);
			val3.anchoredPosition = new Vector2(-5f, -20f);
			textMesh = val2.AddComponent<TextMeshProUGUI>();
			((TMP_Text)textMesh).fontSize = 26f;
			((TMP_Text)textMesh).fontSizeMin = 26f;
			((TMP_Text)textMesh).fontSizeMax = 26f;
			((TMP_Text)textMesh).alignment = (TextAlignmentOptions)260;
			Plugin.Log.LogInfo((object)"Toast Controller Initialized!");
			fontCoroutine = ((MonoBehaviour)this).StartCoroutine(FindFontEventually());
		}

		public void Toast(string text, Color color, float duration = 2f, float fadeTime = 1f)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.CfgShowToasts && !((Object)(object)textMesh == (Object)null))
			{
				if (clearCoroutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(clearCoroutine);
					clearCoroutine = null;
				}
				((TMP_Text)textMesh).text = text;
				((Graphic)textMesh).color = color;
				((TMP_Text)textMesh).alpha = 1f;
				clearCoroutine = ((MonoBehaviour)this).StartCoroutine(ClearToast(duration, fadeTime));
			}
		}

		[IteratorStateMachine(typeof(<ClearToast>d__7))]
		private IEnumerator ClearToast(float duration, float fadeTime = 1f)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <ClearToast>d__7(0)
			{
				<>4__this = this,
				duration = duration,
				fadeTime = fadeTime
			};
		}

		[IteratorStateMachine(typeof(<FindFontEventually>d__8))]
		private IEnumerator FindFontEventually()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <FindFontEventually>d__8(0)
			{
				<>4__this = this
			};
		}
	}
}
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 = 15f;
			}
		}
	}
	[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 = 15f;

		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 = 15f;
				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 = 15f;
			}
			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 = 15f;
			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 <>c__DisplayClass2_0
		{
			public Task<GlobalStatsDto> globalTask;

			public Task<UserStatsDto> userTask;

			internal bool <FetchAndLogStats>b__0()
			{
				if (globalTask.IsCompleted)
				{
					return userTask.IsCompleted;
				}
				return false;
			}
		}

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

			private object <>2__current;

			private <>c__DisplayClass2_0 <>8__1;

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

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

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

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

			private bool MoveNext()
			{
				//IL_0054: Unknown result type (might be due to invalid IL or missing references)
				//IL_005e: Expected O, but got Unknown
				//IL_0074: Unknown result type (might be due to invalid IL or missing references)
				//IL_0079: Unknown result type (might be due to invalid IL or missing references)
				//IL_030d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0312: Unknown result type (might be due to invalid IL or missing references)
				//IL_0362: Unknown result type (might be due to invalid IL or missing references)
				//IL_0348: Unknown result type (might be due to invalid IL or missing references)
				//IL_034d: Unknown result type (might be due to invalid IL or missing references)
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>8__1 = new <>c__DisplayClass2_0();
					<>8__1.globalTask = RemoteApi.FetchGlobalStatsAsync();
					<>8__1.userTask = RemoteApi.FetchUserStatsAsync();
					<>2__current = (object)new WaitUntil((Func<bool>)(() => <>8__1.globalTask.IsCompleted && <>8__1.userTask.IsCompleted));
					<>1__state = 1;
					return true;
				case 1:
				{
					<>1__state = -1;
					string text = "PeakStranding Stats:\n\n";
					Color color = Color.green;
					if (<>8__1.userTask.Status == TaskStatus.RanToCompletion && <>8__1.userTask.Result != null)
					{
						UserStatsDto result = <>8__1.userTask.Result;
						Plugin.Log.LogInfo((object)$"Your stats: {result.TotalStructuresUploaded} structures uploaded (last 24h: {result.StructuresUploadedLast24H}), {result.TotalLikesReceived} likes received, {result.TotalLikesSent} likes sent.");
						text = text + $"You:\nitems uploaded total: {result.TotalStructuresUploaded}\nitems uploaded last day: {result.StructuresUploadedLast24H}\n" + $"likes received: {result.TotalLikesReceived}\nlikes sent: {result.TotalLikesSent}\n";
					}
					else if (<>8__1.userTask.IsFaulted)
					{
						string text2 = "Failed to fetch user stats: " + DescribeTaskFailure(<>8__1.userTask);
						Plugin.Log.LogWarning((object)text2);
					}
					else if (<>8__1.userTask.IsCanceled)
					{
						string text3 = "Failed to fetch user stats: request was cancelled.";
						Plugin.Log.LogWarning((object)text3);
					}
					if (<>8__1.globalTask.Status == TaskStatus.RanToCompletion && <>8__1.globalTask.Result != null)
					{
						GlobalStatsDto result2 = <>8__1.globalTask.Result;
						Plugin.Log.LogInfo((object)$"Global stats: {result2.TotalUniquePlayersAllTime} unique players all-time, {result2.TotalStructuresUploadedAllTime} structures uploaded (last 24h: {result2.TotalStructuresUploadedLast24H} from {result2.TotalUniquePlayersLast24H} players), {result2.TotalLikesGivenAllTime} likes given. Server version {result2.ServerVersion}.");
						text = text + $"\nGlobal:\ntotal items uploaded: {result2.TotalStructuresUploadedAllTime}\nitems uploaded last day: {result2.TotalStructuresUploadedLast24H}\n" + $"total players: {result2.TotalUniquePlayersAllTime}\nplayers last day: {result2.TotalUniquePlayersLast24H}\ntotal likes given: {result2.TotalLikesGivenAllTime}\nserver version: {result2.ServerVersion}";
					}
					else if (<>8__1.globalTask.IsFaulted)
					{
						string text4 = "Failed to fetch global stats: " + DescribeTaskFailure(<>8__1.globalTask);
						Plugin.Log.LogWarning((object)text4);
						color = Color.red;
						text = text + text4 + "\n";
					}
					else if (<>8__1.globalTask.IsCanceled)
					{
						string text5 = "Failed to fetch global stats: request was cancelled.";
						Plugin.Log.LogWarning((object)text5);
						color = Color.red;
						text = text + text5 + "\n";
					}
					ToastController.Instance.Toast(text, color, 15f, 4f);
					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();
			}
		}

		[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()
			{
				//IL_0094: Unknown result type (might be due to invalid IL or missing references)
				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}");
					ToastController.Instance.Toast($"PeakStranding: failed to fetch online items!\n{<task>5__2.Exception}", Color.red, 5f, 3f);
				}
				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.");
				((MonoBehaviour)__instance).StartCoroutine(FetchAndLogStats());
				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
			};
		}

		[IteratorStateMachine(typeof(<FetchAndLogStats>d__2))]
		private static IEnumerator FetchAndLogStats()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <FetchAndLogStats>d__2(0);
		}

		private static string DescribeTaskFailure(Task task)
		{
			return task.Exception?.GetBaseException().Message ?? "Unknown error";
		}
	}
}
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 GlobalStatsUrl => GetBaseUrl() + "/stats/global";

		internal static string UserStatsUrl => GetBaseUrl() + "/stats/me";

		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<GlobalStatsDto> FetchGlobalStatsAsync()
		{
			return await ExecuteWithAuthRetry(async delegate(string authTicket)
			{
				Plugin.Log.LogInfo((object)"Fetching global stats from remote");
				using HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Get, GlobalStatsUrl);
				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);
				}
				GlobalStatsDto globalStatsDto = JsonConvert.DeserializeObject<GlobalStatsDto>(await resp.Content.ReadAsStringAsync().ConfigureAwait(continueOnCapturedContext: false));
				if (globalStatsDto == null)
				{
					throw new InvalidOperationException("Failed to deserialize global stats response.");
				}
				return globalStatsDto;
			});
		}

		public static async Task<UserStatsDto> FetchUserStatsAsync()
		{
			return await ExecuteWithAuthRetry(async delegate(string authTicket)
			{
				Plugin.Log.LogInfo((object)"Fetching user stats from remote");
				using HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Get, UserStatsUrl);
				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);
				}
				UserStatsDto userStatsDto = JsonConvert.DeserializeObject<UserStatsDto>(await resp.Content.ReadAsStringAsync().ConfigureAwait(continueOnCapturedContext: false));
				if (userStatsDto == null)
				{
					throw new InvalidOperationException("Failed to deserialize user stats response.");
				}
				return userStatsDto;
			});
		}

		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);
		}
	}
	internal class GlobalStatsDto
	{
		[JsonProperty("total_unique_players_all_time")]
		public long TotalUniquePlayersAllTime { get; set; }

		[JsonProperty("total_structures_uploaded_all_time")]
		public long TotalStructuresUploadedAllTime { get; set; }

		[JsonProperty("total_likes_given_all_time")]
		public long TotalLikesGivenAllTime { get; set; }

		[JsonProperty("total_unique_players_last_24h")]
		public long TotalUniquePlayersLast24H { get; set; }

		[JsonProperty("total_structures_uploaded_last_24h")]
		public long TotalStructuresUploadedLast24H { get; set; }

		[JsonProperty("server_version")]
		public string ServerVersion { get; set; } = string.Empty;

	}
	internal class UserStatsDto
	{
		[JsonProperty("total_structures_uploaded")]
		public long TotalStructuresUploaded { get; set; }

		[JsonProperty("structures_uploaded_last_24h")]
		public long StructuresUploadedLast24H { get; set; }

		[JsonProperty("total_likes_received")]
		public long TotalLikesReceived { get; set; }

		[JsonProperty("total_likes_sent")]
		public long TotalLikesSent { get; set; }
	}
	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)component