Decompiled source of FollowMe Peak v0.1.0

FollowMePeak.dll

Decompiled a year ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Logging;
using FollowMePeak.Managers;
using FollowMePeak.Models;
using FollowMePeak.Patches;
using FollowMePeak.Services;
using FollowMePeak.UI;
using FollowMePeak.Utils;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
using Zorro.Core;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("FollowMePeak")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("FollowMe-Peak - Climb tracking plugin for Content Warning")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+5a6492bc5ccbf2b487c8bb1fe96530e754533871")]
[assembly: AssemblyProduct("FollowMePeak")]
[assembly: AssemblyTitle("FollowMePeak")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace FollowMePeak
{
	[BepInPlugin("com.thomasaushh.followmepeak", "FollowMe-Peak", "0.1.0")]
	public class Plugin : BaseUnityPlugin
	{
		[CompilerGenerated]
		private sealed class <InitializePathSystem>d__19 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public Plugin <>4__this;

			public Scene scene;

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

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

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

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

			private bool MoveNext()
			{
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				//IL_002e: Expected O, but got Unknown
				//IL_0060: Unknown result type (might be due to invalid IL or missing references)
				int num = <>1__state;
				Plugin CS$<>8__locals0 = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (object)new WaitForSeconds(0.5f);
					<>1__state = 1;
					return true;
				case 1:
				{
					<>1__state = -1;
					NextLevelService service = GameHandler.GetService<NextLevelService>();
					if (service != null && service.Data.IsSome)
					{
						int currentLevelIndex = service.Data.Value.CurrentLevelIndex;
						CS$<>8__locals0._climbDataService.CurrentLevelID = $"{((Scene)(ref scene)).name}_{currentLevelIndex}";
						((BaseUnityPlugin)CS$<>8__locals0).Logger.LogInfo((object)("Level erkannt: " + CS$<>8__locals0._climbDataService.CurrentLevelID));
						CS$<>8__locals0._climbDataService.LoadClimbsFromFile();
						if (CS$<>8__locals0._serverConfigService.Config.EnableCloudSync && CS$<>8__locals0._serverConfigService.Config.AutoDownload)
						{
							CS$<>8__locals0._climbDownloadService.DownloadAndMergeClimbs(CS$<>8__locals0._climbDataService.CurrentLevelID, delegate(int count, string error)
							{
								if (error == null && count > 0)
								{
									((BaseUnityPlugin)CS$<>8__locals0).Logger.LogInfo((object)$"Downloaded {count} new climbs from cloud for level {CS$<>8__locals0._climbDataService.CurrentLevelID}");
									CS$<>8__locals0._visualizationManager.InitializeClimbVisibility();
								}
								else if (error != null && error != "Cloud sync disabled")
								{
									((BaseUnityPlugin)CS$<>8__locals0).Logger.LogWarning((object)("Cloud download failed: " + error));
								}
							});
						}
						CS$<>8__locals0._visualizationManager.InitializeClimbVisibility();
					}
					else
					{
						((BaseUnityPlugin)CS$<>8__locals0).Logger.LogError((object)"NextLevelService or its data could not be found!");
						CS$<>8__locals0._climbDataService.CurrentLevelID = ((Scene)(ref scene)).name + "_unknown";
					}
					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 ClimbDataService _climbDataService;

		private ClimbRecordingManager _recordingManager;

		private ClimbVisualizationManager _visualizationManager;

		private FollowMeUI _ui;

		private ServerConfigService _serverConfigService;

		private VPSApiService _vpsApiService;

		private ClimbUploadService _climbUploadService;

		private ClimbDownloadService _climbDownloadService;

		public static Plugin Instance { get; private set; }

		public ClimbDataService ClimbDataService => _climbDataService;

		private void Awake()
		{
			Instance = this;
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Plugin " + ((BaseUnityPlugin)this).Info.Metadata.GUID + " loaded!"));
			InitializeServices();
			SceneManager.sceneLoaded += OnSceneLoaded;
			Harmony.CreateAndPatchAll(typeof(PluginPatches), (string)null);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Harmony Patches applied.");
		}

		private void InitializeServices()
		{
			_climbDataService = new ClimbDataService(((BaseUnityPlugin)this).Logger);
			_recordingManager = new ClimbRecordingManager(_climbDataService, ((BaseUnityPlugin)this).Logger, (MonoBehaviour)(object)this);
			_visualizationManager = new ClimbVisualizationManager(_climbDataService);
			_serverConfigService = new ServerConfigService(((BaseUnityPlugin)this).Logger);
			_vpsApiService = new VPSApiService(((BaseUnityPlugin)this).Logger, _serverConfigService.Config, (MonoBehaviour)(object)this);
			_climbUploadService = new ClimbUploadService(((BaseUnityPlugin)this).Logger, _vpsApiService, _serverConfigService);
			_climbDownloadService = new ClimbDownloadService(((BaseUnityPlugin)this).Logger, _vpsApiService, _serverConfigService, _climbDataService);
			_ui = new FollowMeUI(_climbDataService, _visualizationManager, _serverConfigService, _vpsApiService, _climbUploadService, _climbDownloadService);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"All services initialized successfully");
			if (_serverConfigService.Config.EnableCloudSync)
			{
				_vpsApiService.CheckServerHealth(delegate(bool isHealthy)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Initial server health check: " + (isHealthy ? "Connected" : "Failed")));
				});
			}
		}

		private void Update()
		{
			if (Input.GetKeyDown((KeyCode)282))
			{
				_ui.ShowMenu = !_ui.ShowMenu;
			}
		}

		private void OnGUI()
		{
			_ui.OnGUI();
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			if (((Scene)(ref scene)).name.StartsWith("Level_"))
			{
				((MonoBehaviour)this).StartCoroutine(InitializePathSystem(scene));
				_recordingManager.StartRecording();
			}
			else
			{
				_climbDataService.CurrentLevelID = "";
				_visualizationManager.ClearVisuals();
			}
		}

		[IteratorStateMachine(typeof(<InitializePathSystem>d__19))]
		private IEnumerator InitializePathSystem(Scene scene)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <InitializePathSystem>d__19(0)
			{
				<>4__this = this,
				scene = scene
			};
		}

		public void OnCampfireLit(string biomeName)
		{
			_recordingManager.SaveCurrentClimb(biomeName);
			_recordingManager.StartRecording();
		}

		public void ShowTagSelectionForNewClimb(ClimbData climbData)
		{
			if (_serverConfigService.Config.EnableCloudSync && _serverConfigService.Config.AutoUpload)
			{
				_climbUploadService.QueueForUpload(climbData, _climbDataService.CurrentLevelID);
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Queued climb for upload: {climbData.Id}");
			}
		}
	}
}
namespace FollowMePeak.Utils
{
	public static class InputValidator
	{
		public static string SanitizePlayerName(string input)
		{
			if (string.IsNullOrWhiteSpace(input))
			{
				return "Anonymous";
			}
			string text = Regex.Replace(input, "[^a-zA-Z0-9_\\-\\s]", "");
			if (text.Length > 50)
			{
				text = text.Substring(0, 50);
			}
			if (!string.IsNullOrWhiteSpace(text))
			{
				return text.Trim();
			}
			return "Anonymous";
		}

		public static string SanitizeBiomeName(string input)
		{
			if (string.IsNullOrWhiteSpace(input))
			{
				return "Unknown";
			}
			string text = Regex.Replace(input, "[^a-zA-Z0-9_\\-\\s]", "");
			if (text.Length > 100)
			{
				text = text.Substring(0, 100);
			}
			if (!string.IsNullOrWhiteSpace(text))
			{
				return text.Trim();
			}
			return "Unknown";
		}

		public static bool IsValidLevelId(string levelId)
		{
			if (string.IsNullOrWhiteSpace(levelId))
			{
				return false;
			}
			return Regex.IsMatch(levelId, "^[a-zA-Z0-9_\\-\\.]{1,100}$");
		}

		public static bool IsValidPeakCode(string peakCode)
		{
			if (string.IsNullOrWhiteSpace(peakCode))
			{
				return false;
			}
			return Regex.IsMatch(peakCode, "^[A-Z0-9]{8}$");
		}

		public static string SanitizePeakCode(string input)
		{
			if (string.IsNullOrWhiteSpace(input))
			{
				return "";
			}
			string text = Regex.Replace(input.ToUpper(), "[^A-Z0-9]", "");
			if (text.Length != 8)
			{
				return "";
			}
			return text;
		}

		public static float ClampDuration(float duration)
		{
			return Math.Max(1f, Math.Min(3600f, duration));
		}

		public static bool IsValidPointCount(int pointCount)
		{
			if (pointCount >= 2)
			{
				return pointCount <= 10000;
			}
			return false;
		}
	}
}
namespace FollowMePeak.UI
{
	public class FollowMeUI
	{
		private enum SortColumn
		{
			CreationTime,
			BiomeName,
			Duration,
			PlayerName
		}

		private readonly ClimbDataService _climbDataService;

		private readonly ClimbVisualizationManager _visualizationManager;

		private readonly CloudSyncUI _cloudSyncUI;

		private readonly VPSApiService _vpsApiService;

		private bool _showMenu;

		private Rect _menuRect = new Rect(20f, 20f, 800f, 600f);

		private Vector2 _scrollPosition = Vector2.zero;

		private CursorLockMode _originalCursorLockState;

		private bool _originalCursorVisible;

		private string _biomeFilter = "";

		private string _dateFilter = "";

		private string _nameFilter = "";

		private string _peakCodeSearch = "";

		private bool _showOnlyLocalPaths;

		private SortColumn _currentSortColumn;

		private bool _sortAscending;

		private int _selectedTab;

		private readonly string[] _tabNames = new string[2] { "My Climbs", "Online Share" };

		public bool ShowMenu
		{
			get
			{
				return _showMenu;
			}
			set
			{
				if (_showMenu != value)
				{
					_showMenu = value;
					UpdateCursorState();
				}
			}
		}

		public FollowMeUI(ClimbDataService climbDataService, ClimbVisualizationManager visualizationManager, ServerConfigService serverConfigService, VPSApiService vpsApiService, ClimbUploadService climbUploadService, ClimbDownloadService climbDownloadService)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			_climbDataService = climbDataService;
			_visualizationManager = visualizationManager;
			_vpsApiService = vpsApiService;
			_cloudSyncUI = new CloudSyncUI(serverConfigService, vpsApiService, climbUploadService, climbDownloadService);
		}

		public void OnGUI()
		{
			//IL_001c: 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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Expected O, but got Unknown
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			if (_showMenu)
			{
				GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.9f);
				_menuRect = GUILayout.Window(0, _menuRect, new WindowFunction(DrawMenuWindow), "FollowMe-Peak - Journal", Array.Empty<GUILayoutOption>());
			}
		}

		private void DrawMenuWindow(int windowID)
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			for (int i = 0; i < _tabNames.Length; i++)
			{
				bool flag = i == _selectedTab;
				if (GUILayout.Toggle(flag, _tabNames[i], GUIStyle.op_Implicit("Button"), Array.Empty<GUILayoutOption>()) && !flag)
				{
					_selectedTab = i;
				}
			}
			GUILayout.EndHorizontal();
			GUILayout.Space(10f);
			switch (_selectedTab)
			{
			case 0:
				DrawClimbJournalTab();
				break;
			case 1:
				DrawCloudSyncTab();
				break;
			}
			GUI.DragWindow(new Rect(0f, 0f, 10000f, 20f));
		}

		private void DrawClimbJournalTab()
		{
			//IL_02e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.BeginVertical(GUIStyle.op_Implicit("box"), Array.Empty<GUILayoutOption>());
			GUILayout.Label("Search & Filter", Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label("Name:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) });
			_nameFilter = GUILayout.TextField(_nameFilter, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) });
			GUILayout.Label("Biome:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) });
			_biomeFilter = GUILayout.TextField(_biomeFilter, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) });
			GUILayout.Label("Peak Code:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) });
			_peakCodeSearch = GUILayout.TextField(_peakCodeSearch, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) });
			GUILayout.EndHorizontal();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("Search", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) }) && !string.IsNullOrEmpty(_peakCodeSearch) && _peakCodeSearch.Length >= 4)
			{
				SearchForPeakCode(_peakCodeSearch);
			}
			if (GUILayout.Button("Clear Filters", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) }))
			{
				_nameFilter = "";
				_biomeFilter = "";
				_peakCodeSearch = "";
				_showOnlyLocalPaths = false;
			}
			GUILayout.FlexibleSpace();
			bool flag = GUILayout.Toggle(_showOnlyLocalPaths, "Local climbs only", Array.Empty<GUILayoutOption>());
			if (flag != _showOnlyLocalPaths)
			{
				_showOnlyLocalPaths = flag;
			}
			GUILayout.EndHorizontal();
			GUILayout.EndVertical();
			GUILayout.BeginHorizontal(GUIStyle.op_Implicit("box"), Array.Empty<GUILayoutOption>());
			GUILayout.Label("Vis", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(30f) });
			if (GUILayout.Button(GetSortLabel("Name", SortColumn.PlayerName), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) }))
			{
				SetSortColumn(SortColumn.PlayerName);
			}
			if (GUILayout.Button(GetSortLabel("Biome", SortColumn.BiomeName), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) }))
			{
				SetSortColumn(SortColumn.BiomeName);
			}
			if (GUILayout.Button(GetSortLabel("Duration", SortColumn.Duration), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }))
			{
				SetSortColumn(SortColumn.Duration);
			}
			if (GUILayout.Button(GetSortLabel("Date", SortColumn.CreationTime), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }))
			{
				SetSortColumn(SortColumn.CreationTime);
			}
			GUILayout.Label("Peak Code", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) });
			GUILayout.Label("Actions", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) });
			GUILayout.EndHorizontal();
			_scrollPosition = GUILayout.BeginScrollView(_scrollPosition, Array.Empty<GUILayoutOption>());
			List<ClimbData> processedClimbs = GetProcessedClimbs();
			List<Guid> list = new List<Guid>();
			foreach (ClimbData item in processedClimbs)
			{
				item.GenerateShareCode();
				DrawClimbTableRow(item, list);
			}
			if (list.Count > 0)
			{
				_climbDataService.DeleteClimbs(list);
				_visualizationManager.UpdateVisuals();
			}
			GUILayout.EndScrollView();
		}

		private void DrawClimbTableRow(ClimbData climbData, List<Guid> climbsToDelete)
		{
			GUILayout.BeginHorizontal(GUIStyle.op_Implicit("box"), Array.Empty<GUILayoutOption>());
			bool flag = _visualizationManager.IsClimbVisible(climbData.Id);
			if (GUILayout.Toggle(flag, "", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(30f) }) != flag)
			{
				_visualizationManager.ToggleClimbVisibility(climbData.Id);
			}
			string text = climbData.GetDisplayName();
			if (climbData.IsFromCloud)
			{
				text = "C: " + text;
			}
			GUILayout.Label(text, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) });
			string text2 = climbData.BiomeName ?? "Unknown";
			if (text2.Length > 12)
			{
				text2 = text2.Substring(0, 12) + "...";
			}
			GUILayout.Label(text2, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) });
			TimeSpan timeSpan = TimeSpan.FromSeconds(climbData.DurationInSeconds);
			GUILayout.Label($"{timeSpan.Minutes:D2}:{timeSpan.Seconds:D2}", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) });
			GUILayout.Label(climbData.CreationTime.ToString("dd.MM"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) });
			GUILayout.Label(climbData.ShareCode, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) });
			GUILayout.Label("", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) });
			GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) });
			if (GUILayout.Button("Copy", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) }))
			{
				Debug.Log((object)("Peak code copied: " + climbData.ShareCode));
				GUIUtility.systemCopyBuffer = climbData.ShareCode;
			}
			if (GUILayout.Button("Del", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(35f) }))
			{
				climbsToDelete.Add(climbData.Id);
			}
			GUILayout.EndHorizontal();
			GUILayout.EndHorizontal();
		}

		private void ShowOnlyCloudClimbs()
		{
			foreach (ClimbData allClimb in _climbDataService.GetAllClimbs())
			{
				if (allClimb.IsFromCloud)
				{
					_visualizationManager.SetClimbVisibility(allClimb.Id, visible: true);
				}
				else
				{
					_visualizationManager.SetClimbVisibility(allClimb.Id, visible: false);
				}
			}
		}

		private void ShowOnlyLocalClimbs()
		{
			foreach (ClimbData allClimb in _climbDataService.GetAllClimbs())
			{
				if (!allClimb.IsFromCloud)
				{
					_visualizationManager.SetClimbVisibility(allClimb.Id, visible: true);
				}
				else
				{
					_visualizationManager.SetClimbVisibility(allClimb.Id, visible: false);
				}
			}
		}

		private void DrawCloudSyncTab()
		{
			_cloudSyncUI.OnGUI();
		}

		private List<ClimbData> GetProcessedClimbs()
		{
			IEnumerable<ClimbData> source = _climbDataService.GetAllClimbs();
			if (!string.IsNullOrWhiteSpace(_biomeFilter))
			{
				source = source.Where((ClimbData p) => (p.BiomeName ?? "").IndexOf(_biomeFilter, StringComparison.OrdinalIgnoreCase) >= 0);
			}
			if (!string.IsNullOrWhiteSpace(_dateFilter))
			{
				source = source.Where((ClimbData p) => p.CreationTime.ToString("dd.MM.yy").Contains(_dateFilter));
			}
			if (!string.IsNullOrWhiteSpace(_nameFilter))
			{
				source = source.Where((ClimbData p) => p.GetDisplayName().IndexOf(_nameFilter, StringComparison.OrdinalIgnoreCase) >= 0);
			}
			if (!string.IsNullOrWhiteSpace(_peakCodeSearch))
			{
				source = source.Where((ClimbData p) => (p.ShareCode ?? "").IndexOf(_peakCodeSearch, StringComparison.OrdinalIgnoreCase) >= 0);
			}
			if (_showOnlyLocalPaths)
			{
				source = source.Where((ClimbData p) => !p.IsFromCloud);
			}
			switch (_currentSortColumn)
			{
			case SortColumn.BiomeName:
				source = (_sortAscending ? source.OrderBy((ClimbData p) => p.BiomeName) : source.OrderByDescending((ClimbData p) => p.BiomeName));
				break;
			case SortColumn.Duration:
				source = (_sortAscending ? source.OrderBy((ClimbData p) => p.DurationInSeconds) : source.OrderByDescending((ClimbData p) => p.DurationInSeconds));
				break;
			case SortColumn.CreationTime:
				source = (_sortAscending ? source.OrderBy((ClimbData p) => p.CreationTime) : source.OrderByDescending((ClimbData p) => p.CreationTime));
				break;
			case SortColumn.PlayerName:
				source = (_sortAscending ? source.OrderBy((ClimbData p) => p.GetDisplayName()) : source.OrderByDescending((ClimbData p) => p.GetDisplayName()));
				break;
			}
			return source.ToList();
		}

		private void SetSortColumn(SortColumn column)
		{
			if (_currentSortColumn == column)
			{
				_sortAscending = !_sortAscending;
				return;
			}
			_currentSortColumn = column;
			_sortAscending = true;
		}

		private string GetSortLabel(string label, SortColumn column)
		{
			if (_currentSortColumn != column)
			{
				return label;
			}
			return label + (_sortAscending ? " ▲" : " ▼");
		}

		private void SearchForPeakCode(string peakCode)
		{
			Debug.Log((object)("Searching for peak code: " + peakCode));
			ClimbData climbData = _climbDataService.GetAllClimbs().FirstOrDefault((ClimbData p) => p.ShareCode != null && p.ShareCode.IndexOf(peakCode, StringComparison.OrdinalIgnoreCase) >= 0);
			if (climbData != null)
			{
				_visualizationManager.SetClimbVisibility(climbData.Id, visible: true);
				Debug.Log((object)("Peak code found: " + climbData.GetDisplayName()));
			}
			else
			{
				Debug.Log((object)("Peak code '" + peakCode + "' not found locally. Searching on server..."));
				SearchForPeakCodeOnServer(peakCode);
			}
		}

		private void SearchForPeakCodeOnServer(string peakCode)
		{
			if (_vpsApiService == null)
			{
				Debug.LogError((object)"VPS API Service not available for server search");
				return;
			}
			_vpsApiService.SearchClimbByPeakCode(peakCode, delegate(ClimbData climbData, string error)
			{
				if (climbData != null)
				{
					climbData.IsFromCloud = true;
					_climbDataService.AddClimb(climbData);
					_visualizationManager.UpdateVisuals();
					_visualizationManager.SetClimbVisibility(climbData.Id, visible: true);
					Debug.Log((object)("Peak code '" + peakCode + "' found on server: " + climbData.GetDisplayName()));
				}
				else
				{
					Debug.Log((object)("Peak code '" + peakCode + "' not found. " + (error ?? "Climb does not exist.")));
				}
			});
		}

		private void UpdateCursorState()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			if (_showMenu)
			{
				if ((int)Cursor.lockState != 0)
				{
					_originalCursorLockState = Cursor.lockState;
					_originalCursorVisible = Cursor.visible;
					Cursor.lockState = (CursorLockMode)0;
					Cursor.visible = true;
				}
			}
			else
			{
				Cursor.lockState = _originalCursorLockState;
				Cursor.visible = _originalCursorVisible;
			}
		}
	}
	public class CloudSyncUI
	{
		private readonly ServerConfigService _configService;

		private readonly VPSApiService _apiService;

		private readonly ClimbUploadService _uploadService;

		private readonly ClimbDownloadService _downloadService;

		private string _playerNameInput = "";

		public CloudSyncUI(ServerConfigService configService, VPSApiService apiService, ClimbUploadService uploadService, ClimbDownloadService downloadService)
		{
			_configService = configService;
			_apiService = apiService;
			_uploadService = uploadService;
			_downloadService = downloadService;
			ServerConfig config = _configService.Config;
			_playerNameInput = config.PlayerName;
		}

		public void OnGUI()
		{
			ServerConfig config = _configService.Config;
			GUILayout.BeginVertical(GUIStyle.op_Implicit("box"), Array.Empty<GUILayoutOption>());
			GUILayout.Label("Share Climbs Online", GUI.skin.label, Array.Empty<GUILayoutOption>());
			GUILayout.Space(5f);
			GUI.skin.toggle.fontSize = 14;
			bool flag = GUILayout.Toggle(config.EnableCloudSync, "  Automatically share my climbs with others", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) });
			GUI.skin.toggle.fontSize = 12;
			if (flag != config.EnableCloudSync)
			{
				_configService.SetCloudSyncEnabled(flag);
			}
			GUILayout.Space(10f);
			if (config.EnableCloudSync)
			{
				DrawSimpleCloudStatus();
			}
			else
			{
				GUILayout.BeginVertical(GUIStyle.op_Implicit("box"), Array.Empty<GUILayoutOption>());
				GUILayout.Label("Info: When enabled:", Array.Empty<GUILayoutOption>());
				GUILayout.Label("- Your successful climbs will be automatically shared", Array.Empty<GUILayoutOption>());
				GUILayout.Label("- You will receive climbs from other players", Array.Empty<GUILayoutOption>());
				GUILayout.Label("- Everything happens automatically in the background", Array.Empty<GUILayoutOption>());
				GUILayout.EndVertical();
			}
			if (config.EnableCloudSync)
			{
				GUILayout.Space(10f);
				DrawPlayerNameSetting();
			}
			GUILayout.EndVertical();
		}

		private void DrawPlayerNameSetting()
		{
			GUILayout.BeginVertical(GUIStyle.op_Implicit("box"), Array.Empty<GUILayoutOption>());
			GUILayout.Label("Player name for shared climbs:", Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			_playerNameInput = GUILayout.TextField(_playerNameInput, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) });
			if (GUILayout.Button("Change", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) }) && !string.IsNullOrEmpty(_playerNameInput))
			{
				_configService.SetPlayerName(_playerNameInput);
			}
			GUILayout.EndHorizontal();
			ServerConfig config = _configService.Config;
			GUILayout.Label("Current: " + config.PlayerName, Array.Empty<GUILayoutOption>());
			GUILayout.EndVertical();
		}

		private void DrawSimpleCloudStatus()
		{
			GUILayout.BeginVertical(GUIStyle.op_Implicit("box"), Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			string text = (_apiService.IsServerReachable ? "● Connected" : "● Offline");
			GUILayout.Label("Status: " + text, Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("Test Connection", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) }))
			{
				_apiService.CheckServerHealth();
			}
			GUILayout.EndHorizontal();
			GUILayout.Space(5f);
			GUILayout.Label("Upload Status: " + _uploadService.GetQueueStatus(), Array.Empty<GUILayoutOption>());
			GUILayout.Label("Download Status: " + _downloadService.GetDownloadStats(), Array.Empty<GUILayoutOption>());
			GUILayout.Space(5f);
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("Upload Now", Array.Empty<GUILayoutOption>()))
			{
				_uploadService.ProcessQueue();
			}
			if (GUILayout.Button("Load New Climbs", Array.Empty<GUILayoutOption>()))
			{
				string currentLevelID = Plugin.Instance.ClimbDataService.CurrentLevelID;
				if (!string.IsNullOrEmpty(currentLevelID))
				{
					_downloadService.DownloadAndMergeClimbs(currentLevelID);
				}
			}
			GUILayout.EndHorizontal();
			GUILayout.EndVertical();
		}
	}
}
namespace FollowMePeak.Patches
{
	public class PluginPatches
	{
		public static string BiomeNameOfCompletedSegment { get; set; }

		[HarmonyPatch(typeof(Campfire), "Light_Rpc")]
		[HarmonyPrefix]
		public static void CaptureBiomeNameBeforeCompletion()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Singleton<MapHandler>.Instance != (Object)null)
			{
				Segment currentSegment = Singleton<MapHandler>.Instance.GetCurrentSegment();
				BiomeNameOfCompletedSegment = Enum.GetName(typeof(Segment), currentSegment);
				Debug.Log((object)("[FollowMe-Peak] Biome captured: " + BiomeNameOfCompletedSegment));
			}
		}

		[HarmonyPatch(typeof(Campfire), "Light_Rpc")]
		[HarmonyPostfix]
		public static void SavePathAfterCampfireLit()
		{
			if ((Object)(object)Plugin.Instance != (Object)null)
			{
				Debug.Log((object)"[FollowMe-Peak] Campfire.Light_Rpc() completed. Saving triggered.");
				Plugin.Instance.OnCampfireLit(BiomeNameOfCompletedSegment);
			}
		}
	}
}
namespace FollowMePeak.Services
{
	public class ClimbDataService
	{
		private readonly ManualLogSource _logger;

		private List<ClimbData> _allLoadedClimbs = new List<ClimbData>();

		private string _currentLevelID = "";

		public string CurrentLevelID
		{
			get
			{
				return _currentLevelID;
			}
			set
			{
				_currentLevelID = value;
			}
		}

		public ClimbDataService(ManualLogSource logger)
		{
			_logger = logger;
		}

		public List<ClimbData> GetAllClimbs()
		{
			return _allLoadedClimbs;
		}

		public void AddClimb(ClimbData climbData)
		{
			_allLoadedClimbs.Add(climbData);
		}

		public void DeleteClimbs(List<Guid> climbIds)
		{
			_allLoadedClimbs.RemoveAll((ClimbData c) => climbIds.Contains(c.Id));
			SaveClimbsToFile(addNewClimb: false);
		}

		public void SaveClimbsToFile(bool addNewClimb = true)
		{
			if (string.IsNullOrEmpty(_currentLevelID) || _currentLevelID.EndsWith("_unknown"))
			{
				return;
			}
			try
			{
				string text = Path.Combine(Paths.PluginPath, "FollowMePeak_Data");
				Directory.CreateDirectory(text);
				string text2 = Path.Combine(text, _currentLevelID + ".json");
				string contents = JsonConvert.SerializeObject((object)_allLoadedClimbs, (Formatting)1);
				File.WriteAllText(text2, contents);
				_logger.LogInfo((object)$"Successfully saved {_allLoadedClimbs.Count} climbs to '{text2}'.");
			}
			catch (Exception arg)
			{
				_logger.LogError((object)$"Error saving climbs: {arg}");
			}
		}

		public void LoadClimbsFromFile()
		{
			_allLoadedClimbs.Clear();
			if (string.IsNullOrEmpty(_currentLevelID) || _currentLevelID.EndsWith("_unknown"))
			{
				return;
			}
			string path = Path.Combine(Paths.PluginPath, "FollowMePeak_Data", _currentLevelID + ".json");
			if (!File.Exists(path))
			{
				_logger.LogInfo((object)("No climb file found for '" + _currentLevelID + "'."));
				return;
			}
			try
			{
				string text = File.ReadAllText(path);
				_allLoadedClimbs = JsonConvert.DeserializeObject<List<ClimbData>>(text) ?? new List<ClimbData>();
				_logger.LogInfo((object)$"{_allLoadedClimbs.Count} climbs loaded for level '{_currentLevelID}'.");
			}
			catch (Exception ex)
			{
				_logger.LogError((object)("Error loading climbs (possibly old format?): " + ex.Message));
			}
		}

		public void ClearClimbs()
		{
			_allLoadedClimbs.Clear();
		}
	}
	public class ClimbDownloadService
	{
		private readonly ManualLogSource _logger;

		private readonly VPSApiService _apiService;

		private readonly ServerConfigService _configService;

		private readonly ClimbDataService _climbDataService;

		private DateTime _lastDownload = DateTime.MinValue;

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

		public bool IsDownloading { get; private set; }

		public DateTime LastDownload => _lastDownload;

		public ClimbDownloadService(ManualLogSource logger, VPSApiService apiService, ServerConfigService configService, ClimbDataService climbDataService)
		{
			_logger = logger;
			_apiService = apiService;
			_configService = configService;
			_climbDataService = climbDataService;
		}

		public void DownloadAndMergeClimbs(string levelId, Action<int, string> callback = null)
		{
			if (!_configService.Config.EnableCloudSync || !_configService.Config.AutoDownload)
			{
				_logger.LogInfo((object)"Cloud sync or auto-download disabled, skipping download");
				callback?.Invoke(0, "Cloud sync disabled");
				return;
			}
			if (IsDownloading)
			{
				_logger.LogInfo((object)"Download already in progress");
				callback?.Invoke(0, "Download in progress");
				return;
			}
			if (_levelDownloadTimes.ContainsKey(levelId) && DateTime.Now - _levelDownloadTimes[levelId] < TimeSpan.FromMinutes(5.0))
			{
				_logger.LogInfo((object)("Downloaded " + levelId + " recently, skipping"));
				callback?.Invoke(0, "Downloaded recently");
				return;
			}
			IsDownloading = true;
			_logger.LogInfo((object)("Starting download for level: " + levelId));
			_apiService.DownloadClimbs(levelId, delegate(List<ClimbData> downloadedClimbs, string error)
			{
				IsDownloading = false;
				_lastDownload = DateTime.Now;
				_levelDownloadTimes[levelId] = DateTime.Now;
				if (error != null)
				{
					_logger.LogError((object)("Download failed for level " + levelId + ": " + error));
					callback?.Invoke(0, error);
					return;
				}
				try
				{
					int num = MergeDownloadedClimbs(downloadedClimbs, levelId);
					_logger.LogInfo((object)$"Downloaded and merged {num} new climbs for level {levelId}");
					callback?.Invoke(num, null);
				}
				catch (Exception ex)
				{
					_logger.LogError((object)("Failed to merge downloaded climbs: " + ex.Message));
					callback?.Invoke(0, ex.Message);
				}
			});
		}

		private int MergeDownloadedClimbs(List<ClimbData> downloadedClimbs, string levelId)
		{
			if (downloadedClimbs == null || downloadedClimbs.Count == 0)
			{
				return 0;
			}
			List<ClimbData> allClimbs = _climbDataService.GetAllClimbs();
			int num = 0;
			foreach (ClimbData downloadedClimb in downloadedClimbs)
			{
				if (!allClimbs.Any((ClimbData x) => x.Id == downloadedClimb.Id))
				{
					if (IsSimilarClimbExists(downloadedClimb, allClimbs))
					{
						_logger.LogDebug((object)$"Skipping similar climb: {downloadedClimb.Id}");
						continue;
					}
					_climbDataService.AddClimb(downloadedClimb);
					num++;
					_logger.LogDebug((object)$"Merged downloaded climb: {downloadedClimb.Id} from {downloadedClimb.BiomeName}");
				}
			}
			if (num > 0)
			{
				_climbDataService.SaveClimbsToFile(addNewClimb: false);
			}
			return num;
		}

		private bool IsSimilarClimbExists(ClimbData newClimb, List<ClimbData> existingClimbs)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: 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_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			foreach (ClimbData existingClimb in existingClimbs)
			{
				if (!(existingClimb.BiomeName != newClimb.BiomeName) && !(Math.Abs(existingClimb.DurationInSeconds - newClimb.DurationInSeconds) > 5f) && newClimb.Points.Count >= 2 && existingClimb.Points.Count >= 2)
				{
					Vector3 val = newClimb.Points.First().ToVector3();
					Vector3 val2 = newClimb.Points.Last().ToVector3();
					Vector3 val3 = existingClimb.Points.First().ToVector3();
					Vector3 val4 = existingClimb.Points.Last().ToVector3();
					float num = Vector3.Distance(val, val3);
					float num2 = Vector3.Distance(val2, val4);
					if (num < 2f && num2 < 2f)
					{
						return true;
					}
				}
			}
			return false;
		}

		public void DownloadRecentClimbs(Action<int, string> callback = null)
		{
			if (!_configService.Config.EnableCloudSync)
			{
				callback?.Invoke(0, "Cloud sync disabled");
				return;
			}
			if (IsDownloading)
			{
				callback?.Invoke(0, "Download in progress");
				return;
			}
			IsDownloading = true;
			_logger.LogInfo((object)"Downloading recent climbs from all levels");
			_apiService.DownloadClimbs("recent", delegate(List<ClimbData> recentClimbs, string error)
			{
				IsDownloading = false;
				_lastDownload = DateTime.Now;
				if (error != null)
				{
					_logger.LogError((object)("Failed to download recent climbs: " + error));
					callback?.Invoke(0, error);
					return;
				}
				try
				{
					int num = MergeDownloadedClimbs(recentClimbs, "all_levels");
					_logger.LogInfo((object)$"Downloaded and merged {num} recent climbs");
					callback?.Invoke(num, null);
				}
				catch (Exception ex)
				{
					_logger.LogError((object)("Failed to merge recent climbs: " + ex.Message));
					callback?.Invoke(0, ex.Message);
				}
			});
		}

		public void CheckForUpdates(string levelId)
		{
			if (!_configService.Config.EnableCloudSync || !_configService.Config.AutoDownload || (_levelDownloadTimes.ContainsKey(levelId) && DateTime.Now - _levelDownloadTimes[levelId] < TimeSpan.FromMinutes(10.0)))
			{
				return;
			}
			DownloadAndMergeClimbs(levelId, delegate(int count, string error)
			{
				if (error == null && count > 0)
				{
					_logger.LogInfo((object)$"Auto-update found {count} new climbs for {levelId}");
				}
			});
		}

		public string GetDownloadStats()
		{
			int count = _levelDownloadTimes.Count;
			string arg = ((_lastDownload == DateTime.MinValue) ? "Never" : $"{(DateTime.Now - _lastDownload).TotalMinutes:F0}m ago");
			return $"Downloads: {count} levels, last: {arg}";
		}

		public void ClearDownloadHistory()
		{
			_levelDownloadTimes.Clear();
			_lastDownload = DateTime.MinValue;
			_logger.LogInfo((object)"Download history cleared");
		}
	}
	public class ClimbUploadService
	{
		[CompilerGenerated]
		private sealed class <WaitAndProcessNext>d__16 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public float delay;

			public ClimbUploadService <>4__this;

			public List<UploadQueueItem> items;

			public int nextIndex;

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

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

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

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

			private bool MoveNext()
			{
				//IL_0025: Unknown result type (might be due to invalid IL or missing references)
				//IL_002f: Expected O, but got Unknown
				int num = <>1__state;
				ClimbUploadService climbUploadService = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (object)new WaitForSeconds(delay);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					climbUploadService.ProcessNextItem(items, nextIndex);
					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 readonly ManualLogSource _logger;

		private readonly VPSApiService _apiService;

		private readonly ServerConfigService _configService;

		private readonly string _queueFilePath;

		private List<UploadQueueItem> _uploadQueue = new List<UploadQueueItem>();

		private bool _isProcessingQueue;

		public int QueuedUploads => _uploadQueue.Count((UploadQueueItem x) => x.Status == UploadStatus.Pending || x.Status == UploadStatus.Failed);

		public int CompletedUploads => _uploadQueue.Count((UploadQueueItem x) => x.Status == UploadStatus.Completed);

		public int FailedUploads => _uploadQueue.Count((UploadQueueItem x) => x.Status == UploadStatus.Failed && !x.ShouldRetry());

		public ClimbUploadService(ManualLogSource logger, VPSApiService apiService, ServerConfigService configService)
		{
			_logger = logger;
			_apiService = apiService;
			_configService = configService;
			_queueFilePath = Path.Combine(Paths.PluginPath, "FollowMePeak_Data", "upload_queue.json");
			LoadQueue();
		}

		public void QueueForUpload(ClimbData climbData, string levelId)
		{
			if (!_configService.Config.EnableCloudSync || !_configService.Config.AutoUpload)
			{
				_logger.LogInfo((object)"Cloud sync or auto-upload disabled, skipping upload");
				return;
			}
			if (climbData == null)
			{
				_logger.LogError((object)"Cannot queue null climb data for upload");
				return;
			}
			if (!InputValidator.IsValidLevelId(levelId))
			{
				_logger.LogError((object)("Cannot queue climb - invalid level ID: " + levelId));
				return;
			}
			if (!InputValidator.IsValidPointCount(climbData.Points?.Count ?? 0))
			{
				_logger.LogError((object)$"Cannot queue climb - invalid point count: {climbData.Points?.Count ?? 0}");
				return;
			}
			if (_uploadQueue.Any((UploadQueueItem x) => x.ClimbData.Id == climbData.Id))
			{
				_logger.LogInfo((object)$"Climb {climbData.Id} already in upload queue");
				return;
			}
			UploadQueueItem uploadQueueItem = new UploadQueueItem
			{
				ClimbData = climbData
			};
			uploadQueueItem.ClimbData.BiomeName = climbData.BiomeName + "|" + levelId;
			_uploadQueue.Add(uploadQueueItem);
			SaveQueue();
			_logger.LogInfo((object)$"Added climb {climbData.Id} to upload queue. Queue size: {QueuedUploads}");
			if (!_isProcessingQueue)
			{
				ProcessQueue();
			}
		}

		public void ProcessQueue()
		{
			if (_isProcessingQueue)
			{
				_logger.LogInfo((object)"Upload queue already being processed");
				return;
			}
			_isProcessingQueue = true;
			_logger.LogInfo((object)$"Starting upload queue processing. {QueuedUploads} items to process");
			CleanupExpiredItems();
			List<UploadQueueItem> list = (from x in _uploadQueue
				where x.Status == UploadStatus.Pending || (x.Status == UploadStatus.Failed && x.ShouldRetry())
				orderby x.CreatedAt
				select x).ToList();
			if (list.Count == 0)
			{
				_logger.LogInfo((object)"No items to process in upload queue");
				_isProcessingQueue = false;
			}
			else
			{
				ProcessNextItem(list, 0);
			}
		}

		private void ProcessNextItem(List<UploadQueueItem> items, int index)
		{
			if (index >= items.Count)
			{
				_logger.LogInfo((object)"Upload queue processing completed");
				_isProcessingQueue = false;
				SaveQueue();
				return;
			}
			UploadQueueItem item = items[index];
			if (item == null || item.ClimbData == null)
			{
				_logger.LogWarning((object)$"Skipping invalid upload queue item at index {index}");
				ProcessNextItem(items, index + 1);
				return;
			}
			if (!_configService.Config.CanUpload())
			{
				_logger.LogWarning((object)"Upload rate limit exceeded, pausing queue processing");
				_isProcessingQueue = false;
				return;
			}
			string text = item.ClimbData.BiomeName;
			string levelId = "unknown";
			if (text.Contains("|"))
			{
				string[] array = text.Split('|');
				text = array[0];
				levelId = array[1];
			}
			item.Status = UploadStatus.Uploading;
			item.LastAttempt = DateTime.Now;
			SaveQueue();
			_logger.LogInfo((object)$"Uploading climb {item.ClimbData.Id} (attempt {item.RetryCount + 1})");
			ClimbData climbData = new ClimbData
			{
				Id = item.ClimbData.Id,
				CreationTime = item.ClimbData.CreationTime,
				BiomeName = text,
				DurationInSeconds = item.ClimbData.DurationInSeconds,
				Points = item.ClimbData.Points
			};
			_apiService.UploadClimb(climbData, levelId, delegate(bool success, string error)
			{
				if (success)
				{
					item.Status = UploadStatus.Completed;
					_logger.LogInfo((object)$"Successfully uploaded climb {item.ClimbData.Id}");
				}
				else
				{
					item.RetryCount++;
					item.LastError = error;
					if (item.ShouldRetry(_configService.Config.RetryAttempts))
					{
						item.Status = UploadStatus.Failed;
						_logger.LogWarning((object)$"Upload failed for climb {item.ClimbData.Id}: {error}. Will retry ({item.RetryCount}/{_configService.Config.RetryAttempts})");
					}
					else
					{
						item.Status = UploadStatus.Failed;
						_logger.LogError((object)$"Upload permanently failed for climb {item.ClimbData.Id}: {error}");
					}
				}
				SaveQueue();
				((MonoBehaviour)Plugin.Instance).StartCoroutine(WaitAndProcessNext(items, index + 1, 2f));
			});
		}

		[IteratorStateMachine(typeof(<WaitAndProcessNext>d__16))]
		private IEnumerator WaitAndProcessNext(List<UploadQueueItem> items, int nextIndex, float delay)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <WaitAndProcessNext>d__16(0)
			{
				<>4__this = this,
				items = items,
				nextIndex = nextIndex,
				delay = delay
			};
		}

		public void RetryFailedUploads()
		{
			List<UploadQueueItem> list = _uploadQueue.Where((UploadQueueItem x) => x.Status == UploadStatus.Failed && x.ShouldRetry()).ToList();
			if (list.Count == 0)
			{
				_logger.LogInfo((object)"No failed uploads to retry");
				return;
			}
			_logger.LogInfo((object)$"Retrying {list.Count} failed uploads");
			foreach (UploadQueueItem item in list)
			{
				item.Status = UploadStatus.Pending;
			}
			SaveQueue();
			ProcessQueue();
		}

		public void ClearCompletedUploads()
		{
			int count = _uploadQueue.Count;
			_uploadQueue.RemoveAll((UploadQueueItem x) => x.Status == UploadStatus.Completed);
			int num = count - _uploadQueue.Count;
			if (num > 0)
			{
				_logger.LogInfo((object)$"Cleared {num} completed uploads from queue");
				SaveQueue();
			}
		}

		private void CleanupExpiredItems()
		{
			TimeSpan maxAge = TimeSpan.FromDays(7.0);
			List<UploadQueueItem> list = _uploadQueue.Where((UploadQueueItem x) => x.IsExpired(maxAge)).ToList();
			foreach (UploadQueueItem item in list)
			{
				item.Status = UploadStatus.Expired;
			}
			_uploadQueue.RemoveAll((UploadQueueItem x) => x.Status == UploadStatus.Expired);
			if (list.Count > 0)
			{
				_logger.LogInfo((object)$"Removed {list.Count} expired items from upload queue");
			}
		}

		private void SaveQueue()
		{
			try
			{
				Directory.CreateDirectory(Path.GetDirectoryName(_queueFilePath));
				string contents = JsonConvert.SerializeObject((object)_uploadQueue, (Formatting)1);
				File.WriteAllText(_queueFilePath, contents);
			}
			catch (Exception ex)
			{
				_logger.LogError((object)("Failed to save upload queue: " + ex.Message));
			}
		}

		private void LoadQueue()
		{
			try
			{
				if (!File.Exists(_queueFilePath))
				{
					return;
				}
				string text = File.ReadAllText(_queueFilePath);
				_uploadQueue = JsonConvert.DeserializeObject<List<UploadQueueItem>>(text) ?? new List<UploadQueueItem>();
				_uploadQueue.RemoveAll((UploadQueueItem item) => item.ClimbData == null);
				foreach (UploadQueueItem item in _uploadQueue.Where((UploadQueueItem x) => x.Status == UploadStatus.Uploading))
				{
					item.Status = UploadStatus.Pending;
				}
				_logger.LogInfo((object)$"Loaded upload queue with {_uploadQueue.Count} items");
			}
			catch (Exception ex)
			{
				_logger.LogError((object)("Failed to load upload queue: " + ex.Message));
				_uploadQueue = new List<UploadQueueItem>();
			}
		}

		public string GetQueueStatus()
		{
			return $"Queue: {QueuedUploads} pending, {CompletedUploads} completed, {FailedUploads} failed";
		}
	}
	public class ServerConfigService
	{
		private readonly ManualLogSource _logger;

		private readonly string _configPath;

		private ServerConfig _config;

		public ServerConfig Config => _config ?? LoadConfig();

		public ServerConfigService(ManualLogSource logger)
		{
			_logger = logger;
			_configPath = Path.Combine(Paths.PluginPath, "FollowMePeak_Data", "server_config.json");
		}

		public ServerConfig LoadConfig()
		{
			try
			{
				if (File.Exists(_configPath))
				{
					string text = File.ReadAllText(_configPath);
					_config = JsonConvert.DeserializeObject<ServerConfig>(text);
					_logger.LogInfo((object)"Server configuration loaded successfully");
				}
				else
				{
					_config = CreateDefaultConfig();
					SaveConfig();
					_logger.LogInfo((object)"Created default server configuration");
				}
			}
			catch (Exception ex)
			{
				_logger.LogError((object)("Failed to load server config: " + ex.Message));
				_config = CreateDefaultConfig();
			}
			return _config;
		}

		public void SaveConfig()
		{
			try
			{
				Directory.CreateDirectory(Path.GetDirectoryName(_configPath));
				string contents = JsonConvert.SerializeObject((object)_config, (Formatting)1);
				File.WriteAllText(_configPath, contents);
				_logger.LogInfo((object)"Server configuration saved");
			}
			catch (Exception ex)
			{
				_logger.LogError((object)("Failed to save server config: " + ex.Message));
			}
		}

		private ServerConfig CreateDefaultConfig()
		{
			return new ServerConfig
			{
				EnableCloudSync = true,
				PlayerName = GenerateRandomPlayerName(),
				LastUploadReset = DateTime.Now,
				UploadsThisHour = 0
			};
		}

		private string GenerateRandomPlayerName()
		{
			string[] array = new string[70]
			{
				"Quick", "Swift", "Fast", "Rapid", "Lightning", "Turbo", "Blazing", "Speedy", "Nimble", "Agile",
				"Rocky", "Steep", "Alpine", "Icy", "Snowy", "Windy", "High", "Rugged", "Vertical", "Frozen",
				"Summit", "Peak", "Ridge", "Cliff", "Stone", "Granite", "Crystal", "Misty", "Foggy", "Stormy",
				"Brave", "Bold", "Fearless", "Daring", "Wild", "Crazy", "Mad", "Epic", "Legendary", "Mighty",
				"Strong", "Tough", "Hardcore", "Extreme", "Silent", "Clever", "Smart", "Wise", "Sharp", "Keen",
				"Dizzy", "Wobbly", "Clumsy", "Sleepy", "Hungry", "Thirsty", "Lost", "Confused", "Lucky", "Unlucky",
				"Weird", "Strange", "Funky", "Silly", "Goofy", "Bouncy", "Fuzzy", "Sparkly", "Shiny", "Glowing"
			};
			string[] array2 = new string[65]
			{
				"Climber", "Mountaineer", "Alpinist", "Scrambler", "Boulderer", "Summiteer", "Peakbagger", "Ridgewalker", "FollowMe", "Trailblazer",
				"Explorer", "Navigator", "Scout", "Wanderer", "Seeker", "Hiker", "Trekker", "Adventurer", "Voyager", "Pioneer",
				"Ranger", "Guide", "Sherpa", "Basecamp", "Expedition", "Goat", "Yak", "Eagle", "Hawk", "Raven",
				"Bear", "Wolf", "Fox", "Lynx", "Marmot", "Ibex", "Chamois", "Falcon", "Condor", "Vulture",
				"SnowLeopard", "Bighorn", "Pika", "Ptarmigan", "Wolverine", "Potato", "Pretzel", "Pickle", "Pancake", "Waffle",
				"Burrito", "Taco", "Pizza", "Bagel", "Donut", "Penguin", "Llama", "Walrus", "Hedgehog", "Squirrel",
				"Narwhal", "Unicorn", "Dragon", "Phoenix", "Yeti"
			};
			Random random = new Random();
			string arg = array[random.Next(array.Length)];
			string arg2 = array2[random.Next(array2.Length)];
			int num = random.Next(100, 999);
			return $"{arg}{arg2}_{num}";
		}

		public void SetCloudSyncEnabled(bool enabled)
		{
			_config.EnableCloudSync = enabled;
			SaveConfig();
			_logger.LogInfo((object)("Cloud sync " + (enabled ? "enabled" : "disabled")));
		}

		public void SetPlayerName(string playerName)
		{
			if (string.IsNullOrWhiteSpace(playerName))
			{
				playerName = "Anonymous";
			}
			else
			{
				playerName = Regex.Replace(playerName, "[^a-zA-Z0-9_-]", "");
				if (playerName.Length > 50)
				{
					playerName = playerName.Substring(0, 50);
				}
				if (string.IsNullOrWhiteSpace(playerName))
				{
					playerName = "Anonymous";
				}
			}
			_config.PlayerName = playerName;
			SaveConfig();
			_logger.LogInfo((object)("Player name updated to: " + _config.PlayerName));
		}

		public void ResetUploadRateLimit()
		{
			_config.UploadsThisHour = 0;
			_config.LastUploadReset = DateTime.Now;
			SaveConfig();
			_logger.LogInfo((object)"Upload rate limit reset");
		}

		public bool ValidateConfig()
		{
			return true;
		}
	}
	public class VPSApiService
	{
		[CompilerGenerated]
		private sealed class <CheckServerHealthCoroutine>d__13 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public VPSApiService <>4__this;

			public Action<bool> callback;

			private UnityWebRequest <request>5__2;

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

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

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

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || num == 1)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<request>5__2 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
				//IL_00bf: Invalid comparison between Unknown and I4
				try
				{
					int num = <>1__state;
					VPSApiService vPSApiService = <>4__this;
					switch (num)
					{
					default:
						return false;
					case 0:
					{
						<>1__state = -1;
						string text = vPSApiService._config.BaseUrl + "/api/health";
						<request>5__2 = UnityWebRequest.Get(text);
						<>1__state = -3;
						<request>5__2.timeout = vPSApiService._config.TimeoutSeconds;
						<request>5__2.SetRequestHeader("X-API-Key", vPSApiService._config.ApiKey);
						<>2__current = <request>5__2.SendWebRequest();
						<>1__state = 1;
						return true;
					}
					case 1:
						<>1__state = -3;
						vPSApiService.LastHealthCheck = DateTime.Now;
						if ((int)<request>5__2.result == 1)
						{
							try
							{
								HealthResponse healthResponse = JsonConvert.DeserializeObject<HealthResponse>(<request>5__2.downloadHandler.text);
								vPSApiService.IsServerReachable = healthResponse.Status == "healthy";
								if (vPSApiService.IsServerReachable)
								{
									vPSApiService._logger.LogInfo((object)$"Server health check successful. {healthResponse.Stats.TotalClimbs} climbs in database.");
								}
								else
								{
									vPSApiService._logger.LogWarning((object)("Server unhealthy: " + healthResponse.Status));
									vPSApiService.IsServerReachable = false;
								}
							}
							catch (Exception ex)
							{
								vPSApiService._logger.LogError((object)("Failed to parse health response: " + ex.Message));
								vPSApiService.IsServerReachable = false;
							}
						}
						else
						{
							vPSApiService._logger.LogError((object)("Health check failed: " + <request>5__2.error));
							vPSApiService.IsServerReachable = false;
						}
						callback?.Invoke(vPSApiService.IsServerReachable);
						<>m__Finally1();
						<request>5__2 = null;
						return false;
					}
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
			}

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

			private void <>m__Finally1()
			{
				<>1__state = -1;
				if (<request>5__2 != null)
				{
					((IDisposable)<request>5__2).Dispose();
				}
			}

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

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

			private object <>2__current;

			public string levelId;

			public Action<List<ClimbData>, string> callback;

			public VPSApiService <>4__this;

			private UnityWebRequest <request>5__2;

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

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

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

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || num == 1)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<request>5__2 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ee: Invalid comparison between Unknown and I4
				try
				{
					int num = <>1__state;
					VPSApiService vPSApiService = <>4__this;
					switch (num)
					{
					default:
						return false;
					case 0:
					{
						<>1__state = -1;
						if (!InputValidator.IsValidLevelId(levelId))
						{
							callback?.Invoke(new List<ClimbData>(), "Invalid level ID format");
							return false;
						}
						string text3 = vPSApiService._config.BaseUrl + "/api/climbs/" + levelId + "?limit=200";
						<request>5__2 = UnityWebRequest.Get(text3);
						<>1__state = -3;
						<request>5__2.timeout = vPSApiService._config.TimeoutSeconds;
						<request>5__2.SetRequestHeader("X-API-Key", vPSApiService._config.ApiKey);
						<>2__current = <request>5__2.SendWebRequest();
						<>1__state = 1;
						return true;
					}
					case 1:
						<>1__state = -3;
						if ((int)<request>5__2.result == 1)
						{
							try
							{
								ClimbListResponse climbListResponse = JsonConvert.DeserializeObject<ClimbListResponse>(<request>5__2.downloadHandler.text);
								List<ClimbData> list = new List<ClimbData>();
								if (climbListResponse.Data != null)
								{
									foreach (ServerClimbData datum in climbListResponse.Data)
									{
										list.Add(datum.ToClimbData());
									}
								}
								vPSApiService._logger.LogInfo((object)$"Downloaded {list.Count} climbs for level {levelId}");
								callback?.Invoke(list, null);
							}
							catch (Exception ex)
							{
								string text = "Failed to parse download response: " + ex.Message;
								vPSApiService._logger.LogError((object)text);
								callback?.Invoke(new List<ClimbData>(), text);
							}
						}
						else if (<request>5__2.responseCode == 404)
						{
							vPSApiService._logger.LogInfo((object)("No climbs found for level " + levelId));
							callback?.Invoke(new List<ClimbData>(), null);
						}
						else
						{
							string text2 = $"Download request failed: {<request>5__2.error} (HTTP {<request>5__2.responseCode})";
							vPSApiService._logger.LogError((object)text2);
							callback?.Invoke(new List<ClimbData>(), text2);
						}
						<>m__Finally1();
						<request>5__2 = null;
						return false;
					}
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
			}

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

			private void <>m__Finally1()
			{
				<>1__state = -1;
				if (<request>5__2 != null)
				{
					((IDisposable)<request>5__2).Dispose();
				}
			}

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

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

			private object <>2__current;

			public VPSApiService <>4__this;

			public Action<string, string> callback;

			private UnityWebRequest <request>5__2;

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

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

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

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || num == 1)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<request>5__2 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b4: Invalid comparison between Unknown and I4
				try
				{
					int num = <>1__state;
					VPSApiService vPSApiService = <>4__this;
					switch (num)
					{
					default:
						return false;
					case 0:
					{
						<>1__state = -1;
						string text2 = vPSApiService._config.BaseUrl + "/api/stats";
						<request>5__2 = UnityWebRequest.Get(text2);
						<>1__state = -3;
						<request>5__2.timeout = vPSApiService._config.TimeoutSeconds;
						<request>5__2.SetRequestHeader("X-API-Key", vPSApiService._config.ApiKey);
						<>2__current = <request>5__2.SendWebRequest();
						<>1__state = 1;
						return true;
					}
					case 1:
						<>1__state = -3;
						if ((int)<request>5__2.result == 1)
						{
							callback?.Invoke(<request>5__2.downloadHandler.text, null);
						}
						else
						{
							string text = "Stats request failed: " + <request>5__2.error;
							vPSApiService._logger.LogError((object)text);
							callback?.Invoke(null, text);
						}
						<>m__Finally1();
						<request>5__2 = null;
						return false;
					}
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
			}

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

			private void <>m__Finally1()
			{
				<>1__state = -1;
				if (<request>5__2 != null)
				{
					((IDisposable)<request>5__2).Dispose();
				}
			}

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

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

			private object <>2__current;

			public VPSApiService <>4__this;

			public string peakCode;

			public Action<ClimbData, string> callback;

			private UnityWebRequest <request>5__2;

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

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

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

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || num == 1)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<request>5__2 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ba: Invalid comparison between Unknown and I4
				try
				{
					int num = <>1__state;
					VPSApiService vPSApiService = <>4__this;
					switch (num)
					{
					default:
						return false;
					case 0:
					{
						<>1__state = -1;
						string text3 = vPSApiService._config.BaseUrl + "/api/climbs/search/" + peakCode;
						<request>5__2 = UnityWebRequest.Get(text3);
						<>1__state = -3;
						<request>5__2.timeout = vPSApiService._config.TimeoutSeconds;
						<request>5__2.SetRequestHeader("X-API-Key", vPSApiService._config.ApiKey);
						<>2__current = <request>5__2.SendWebRequest();
						<>1__state = 1;
						return true;
					}
					case 1:
						<>1__state = -3;
						if ((int)<request>5__2.result == 1)
						{
							try
							{
								ClimbSearchResponse climbSearchResponse = JsonConvert.DeserializeObject<ClimbSearchResponse>(<request>5__2.downloadHandler.text);
								if (climbSearchResponse.Success && climbSearchResponse.Data != null)
								{
									ClimbData climbData = climbSearchResponse.Data.ToClimbData();
									vPSApiService._logger.LogInfo((object)("Found climb with peak code " + peakCode + ": " + climbData.GetDisplayName()));
									callback?.Invoke(climbData, null);
								}
								else
								{
									vPSApiService._logger.LogInfo((object)("No climb found with peak code " + peakCode));
									callback?.Invoke(null, "Climb not found");
								}
							}
							catch (Exception ex)
							{
								string text = "Failed to parse search response: " + ex.Message;
								vPSApiService._logger.LogError((object)text);
								callback?.Invoke(null, text);
							}
						}
						else if (<request>5__2.responseCode == 404)
						{
							vPSApiService._logger.LogInfo((object)("No climb found with peak code " + peakCode));
							callback?.Invoke(null, "Climb not found");
						}
						else
						{
							string text2 = $"Search request failed: {<request>5__2.error} (HTTP {<request>5__2.responseCode})";
							vPSApiService._logger.LogError((object)text2);
							callback?.Invoke(null, text2);
						}
						<>m__Finally1();
						<request>5__2 = null;
						return false;
					}
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
			}

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

			private void <>m__Finally1()
			{
				<>1__state = -1;
				if (<request>5__2 != null)
				{
					((IDisposable)<request>5__2).Dispose();
				}
			}

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

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

			private object <>2__current;

			public string levelId;

			public Action<bool, string> callback;

			public ClimbData climbData;

			public VPSApiService <>4__this;

			private UnityWebRequest <request>5__2;

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

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

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

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || num == 1)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<request>5__2 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0239: Unknown result type (might be due to invalid IL or missing references)
				//IL_023f: Invalid comparison between Unknown and I4
				//IL_0192: Unknown result type (might be due to invalid IL or missing references)
				//IL_019c: Expected O, but got Unknown
				//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
				//IL_01b6: Expected O, but got Unknown
				//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
				//IL_01c6: Expected O, but got Unknown
				try
				{
					int num = <>1__state;
					VPSApiService vPSApiService = <>4__this;
					switch (num)
					{
					default:
						return false;
					case 0:
					{
						<>1__state = -1;
						if (!InputValidator.IsValidLevelId(levelId))
						{
							callback?.Invoke(arg1: false, "Invalid level ID format");
							return false;
						}
						if (!InputValidator.IsValidPointCount(climbData.Points?.Count ?? 0))
						{
							callback?.Invoke(arg1: false, "Invalid climb data - point count out of range");
							return false;
						}
						string text2 = vPSApiService._config.BaseUrl + "/api/climbs";
						List<ApiVector3> points = climbData.Points.Select((SerializableVector3 p) => p.ToApiVector3()).ToList();
						points = vPSApiService.ReducePointsIfNeeded(points);
						string text3 = JsonConvert.SerializeObject((object)new
						{
							levelId = levelId,
							playerName = InputValidator.SanitizePlayerName(vPSApiService._config.PlayerName),
							biomeName = InputValidator.SanitizeBiomeName(climbData.BiomeName),
							duration = InputValidator.ClampDuration(climbData.DurationInSeconds),
							points = points,
							isSuccessful = true,
							tags = new string[0]
						});
						if (text3.Length > vPSApiService.GetMaxPayloadSize())
						{
							string text4 = $"Payload too large ({text3.Length} bytes). Consider reducing climb complexity.";
							vPSApiService._logger.LogError((object)text4);
							callback?.Invoke(arg1: false, text4);
							return false;
						}
						byte[] bytes = Encoding.UTF8.GetBytes(text3);
						<request>5__2 = new UnityWebRequest(text2, "POST");
						<>1__state = -3;
						<request>5__2.uploadHandler = (UploadHandler)new UploadHandlerRaw(bytes);
						<request>5__2.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
						<request>5__2.SetRequestHeader("Content-Type", "application/json");
						<request>5__2.SetRequestHeader("X-API-Key", vPSApiService._config.ApiKey);
						<request>5__2.timeout = vPSApiService._config.TimeoutSeconds;
						<>2__current = <request>5__2.SendWebRequest();
						<>1__state = 1;
						return true;
					}
					case 1:
						<>1__state = -3;
						if ((int)<request>5__2.result == 1)
						{
							try
							{
								ApiResponse<ClimbUploadResponse> apiResponse = JsonConvert.DeserializeObject<ApiResponse<ClimbUploadResponse>>(<request>5__2.downloadHandler.text);
								if (apiResponse.Success)
								{
									vPSApiService._config.IncrementUploadCount();
									vPSApiService._logger.LogInfo((object)("Climb uploaded successfully: " + apiResponse.Data.ClimbId));
									callback?.Invoke(arg1: true, apiResponse.Data.ClimbId);
								}
								else
								{
									vPSApiService._logger.LogError((object)("Upload failed: " + apiResponse.Error));
									callback?.Invoke(arg1: false, apiResponse.Error);
								}
							}
							catch (Exception ex)
							{
								vPSApiService._logger.LogError((object)("Failed to parse upload response: " + ex.Message));
								callback?.Invoke(arg1: false, "Parse error");
							}
						}
						else
						{
							string text = $"Upload request failed: {<request>5__2.error} (HTTP {<request>5__2.responseCode})";
							vPSApiService._logger.LogError((object)text);
							callback?.Invoke(arg1: false, text);
						}
						<>m__Finally1();
						<request>5__2 = null;
						return false;
					}
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
			}

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

			private void <>m__Finally1()
			{
				<>1__state = -1;
				if (<request>5__2 != null)
				{
					((IDisposable)<request>5__2).Dispose();
				}
			}

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

		private readonly ManualLogSource _logger;

		private readonly ServerConfig _config;

		private readonly MonoBehaviour _coroutineRunner;

		public bool IsServerReachable { get; private set; }

		public DateTime LastHealthCheck { get; private set; } = DateTime.MinValue;


		public VPSApiService(ManualLogSource logger, ServerConfig config, MonoBehaviour coroutineRunner)
		{
			_logger = logger;
			_config = config;
			_coroutineRunner = coroutineRunner;
		}

		public void CheckServerHealth(Action<bool> callback = null)
		{
			_coroutineRunner.StartCoroutine(CheckServerHealthCoroutine(callback));
		}

		[IteratorStateMachine(typeof(<CheckServerHealthCoroutine>d__13))]
		private IEnumerator CheckServerHealthCoroutine(Action<bool> callback)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <CheckServerHealthCoroutine>d__13(0)
			{
				<>4__this = this,
				callback = callback
			};
		}

		public void UploadClimb(ClimbData climbData, string levelId, Action<bool, string> callback)
		{
			if (!_config.EnableCloudSync)
			{
				callback?.Invoke(arg1: false, "Cloud sync disabled");
			}
			else if (!_config.CanUpload())
			{
				callback?.Invoke(arg1: false, "Upload rate limit exceeded");
			}
			else
			{
				_coroutineRunner.StartCoroutine(UploadClimbCoroutine(climbData, levelId, callback));
			}
		}

		[IteratorStateMachine(typeof(<UploadClimbCoroutine>d__15))]
		private IEnumerator UploadClimbCoroutine(ClimbData climbData, string levelId, Action<bool, string> callback)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <UploadClimbCoroutine>d__15(0)
			{
				<>4__this = this,
				climbData = climbData,
				levelId = levelId,
				callback = callback
			};
		}

		public void DownloadClimbs(string levelId, Action<List<ClimbData>, string> callback)
		{
			if (!_config.EnableCloudSync)
			{
				callback?.Invoke(new List<ClimbData>(), "Cloud sync disabled");
			}
			else
			{
				_coroutineRunner.StartCoroutine(DownloadClimbsCoroutine(levelId, callback));
			}
		}

		[IteratorStateMachine(typeof(<DownloadClimbsCoroutine>d__17))]
		private IEnumerator DownloadClimbsCoroutine(string levelId, Action<List<ClimbData>, string> callback)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <DownloadClimbsCoroutine>d__17(0)
			{
				<>4__this = this,
				levelId = levelId,
				callback = callback
			};
		}

		public void SearchClimbByPeakCode(string peakCode, Action<ClimbData, string> callback)
		{
			if (!_config.EnableCloudSync)
			{
				callback?.Invoke(null, "Cloud sync disabled");
				return;
			}
			string peakCode2 = InputValidator.SanitizePeakCode(peakCode);
			if (!InputValidator.IsValidPeakCode(peakCode2))
			{
				callback?.Invoke(null, "Peak code must be 8 alphanumeric characters");
			}
			else
			{
				_coroutineRunner.StartCoroutine(SearchClimbByPeakCodeCoroutine(peakCode2, callback));
			}
		}

		[IteratorStateMachine(typeof(<SearchClimbByPeakCodeCoroutine>d__19))]
		private IEnumerator SearchClimbByPeakCodeCoroutine(string peakCode, Action<ClimbData, string> callback)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <SearchClimbByPeakCodeCoroutine>d__19(0)
			{
				<>4__this = this,
				peakCode = peakCode,
				callback = callback
			};
		}

		public void GetServerStats(Action<string, string> callback)
		{
			_coroutineRunner.StartCoroutine(GetServerStatsCoroutine(callback));
		}

		[IteratorStateMachine(typeof(<GetServerStatsCoroutine>d__21))]
		private IEnumerator GetServerStatsCoroutine(Action<string, string> callback)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <GetServerStatsCoroutine>d__21(0)
			{
				<>4__this = this,
				callback = callback
			};
		}

		private int GetMaxPayloadSize()
		{
			return 2097152;
		}

		private List<ApiVector3> ReducePointsIfNeeded(List<ApiVector3> points)
		{
			if (points.Count <= 2000)
			{
				return points;
			}
			_logger.LogWarning((object)$"Path has {points.Count} points, reducing to {2000} for upload");
			List<ApiVector3> list = new List<ApiVector3>();
			float num = (float)points.Count / 2000f;
			for (int i = 0; i < 2000; i++)
			{
				int num2 = Mathf.RoundToInt((float)i * num);
				if (num2 < points.Count)
				{
					list.Add(points[num2]);
				}
			}
			if (list.Count > 0 && !list.Last().Equals(points.Last()))
			{
				list[list.Count - 1] = points.Last();
			}
			return list;
		}
	}
}
namespace FollowMePeak.Models
{
	public class ApiResponse<T>
	{
		public bool Success { get; set; }

		public T Data { get; set; }

		public string Message { get; set; }

		public string Error { get; set; }
	}
	public class ClimbUploadResponse
	{
		public string ClimbId { get; set; }

		public string LevelId { get; set; }

		public long UploadTime { get; set; }
	}
	public class ClimbListResponse
	{
		public List<ServerClimbData> Data { get; set; }

		public ClimbListMeta Meta { get; set; }
	}
	public class ClimbSearchResponse
	{
		public bool Success { get; set; }

		public ServerClimbData Data { get; set; }

		public string Message { get; set; }

		public string Error { get; set; }
	}
	public class ClimbListMeta
	{
		public string Level_Id { get; set; }

		public int Count { get; set; }

		public int Limit { get; set; }

		public int Offset { get; set; }
	}
	public class ServerClimbData
	{
		public string Id { get; set; }

		public string Level_Id { get; set; }

		public string Player_Name { get; set; }

		public string Biome_Name { get; set; }

		public float Duration { get; set; }

		public List<ApiVector3> Points { get; set; }

		public bool Is_Successful { get; set; }

		public string Share_Code { get; set; }

		public long Upload_Time { get; set; }

		public string Created_At { get; set; }

		public ClimbData ToClimbData()
		{
			List<SerializableVector3> points = Points?.Select((ApiVector3 p) => p.ToSerializableVector3()).ToList() ?? new List<SerializableVector3>();
			ClimbData climbData = new ClimbData
			{
				Id = Guid.Parse(Id),
				CreationTime = DateTime.Parse(Created_At),
				BiomeName = (Biome_Name ?? "Unknown"),
				DurationInSeconds = Duration,
				Points = points,
				PlayerName = (Player_Name ?? "Unknown"),
				IsFromCloud = true
			};
			if (!string.IsNullOrEmpty(Share_Code))
			{
				climbData.ShareCode = Share_Code;
			}
			else
			{
				climbData.GenerateShareCode();
			}
			return climbData;
		}
	}
	public class HealthResponse
	{
		public string Status { get; set; }

		public string Timestamp { get; set; }

		public string Database { get; set; }

		public HealthStats Stats { get; set; }

		public double Uptime { get; set; }

		public string Version { get; set; }
	}
	public class HealthStats
	{
		public int TotalClimbs { get; set; }

		public int TotalLevels { get; set; }
	}
	public class ClimbData
	{
		public Guid Id { get; set; }

		public DateTime CreationTime { get; set; }

		public string BiomeName { get; set; }

		public float DurationInSeconds { get; set; }

		public List<SerializableVector3> Points { get; set; }

		public string SaveName { get; set; } = "";


		public string PlayerName { get; set; } = "Local";


		public bool IsFromCloud { get; set; }

		public string ShareCode { get; set; } = "";


		public string GetDisplayName()
		{
			if (!string.IsNullOrEmpty(SaveName))
			{
				return SaveName;
			}
			TimeSpan timeSpan = TimeSpan.FromSeconds(DurationInSeconds);
			return $"{BiomeName} - {timeSpan.Minutes:D2}m{timeSpan.Seconds:D2}s";
		}

		public void GenerateShareCode()
		{
			if (string.IsNullOrEmpty(ShareCode))
			{
				ShareCode = Id.ToString("N").Substring(0, 8).ToUpper();
			}
		}
	}
	public struct SerializableVector3
	{
		public float X;

		public float Y;

		public float Z;

		public SerializableVector3(Vector3 vec)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			X = vec.x;
			Y = vec.y;
			Z = vec.z;
		}

		public Vector3 ToVector3()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			return new Vector3(X, Y, Z);
		}

		public ApiVector3 ToApiVector3()
		{
			ApiVector3 result = default(ApiVector3);
			result.x = X;
			result.y = Y;
			result.z = Z;
			return result;
		}
	}
	public struct ApiVector3
	{
		public float x;

		public float y;

		public float z;

		public ApiVector3(Vector3 vec)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			x = vec.x;
			y = vec.y;
			z = vec.z;
		}

		public ApiVector3(SerializableVector3 vec)
		{
			x = vec.X;
			y = vec.Y;
			z = vec.Z;
		}

		public Vector3 ToVector3()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			return new Vector3(x, y, z);
		}

		public SerializableVector3 ToSerializableVector3()
		{
			SerializableVector3 result = default(SerializableVector3);
			result.X = x;
			result.Y = y;
			result.Z = z;
			return result;
		}
	}
	public class ServerConfig
	{
		[JsonIgnore]
		public string BaseUrl => "https://followme-peak.ddns.net";

		[JsonIgnore]
		public bool AutoUpload => true;

		[JsonIgnore]
		public bool AutoDownload => true;

		[JsonIgnore]
		public int TimeoutSeconds => 30;

		[JsonIgnore]
		public int RetryAttempts => 3;

		[JsonIgnore]
		public int MaxUploadsPerHour => 50;

		[JsonIgnore]
		public string ApiKey => "FMP_2024_PEAK_CLIMB_AUTH_v1_7B8C9D2E";

		public bool EnableCloudSync { get; set; }

		public string PlayerName { get; set; } = "Anonymous";


		public DateTime LastUploadReset { get; set; } = DateTime.MinValue;


		public int UploadsThisHour { get; set; }

		public bool CanUpload()
		{
			if (DateTime.Now - LastUploadReset > TimeSpan.FromHours(1.0))
			{
				UploadsThisHour = 0;
				LastUploadReset = DateTime.Now;
			}
			return UploadsThisHour < MaxUploadsPerHour;
		}

		public void IncrementUploadCount()
		{
			UploadsThisHour++;
		}
	}
	public class UploadQueueItem
	{
		public Guid Id { get; set; } = Guid.NewGuid();


		public ClimbData ClimbData { get; set; }

		public int RetryCount { get; set; }

		public DateTime CreatedAt { get; set; } = DateTime.Now;


		public DateTime? LastAttempt { get; set; }

		public string LastError { get; set; }

		public UploadStatus Status { get; set; }

		public bool ShouldRetry(int maxRetries = 3)
		{
			if (RetryCount < maxRetries)
			{
				return Status == UploadStatus.Failed;
			}
			return false;
		}

		public bool IsExpired(TimeSpan maxAge)
		{
			return DateTime.Now - CreatedAt > maxAge;
		}
	}
	public enum UploadStatus
	{
		Pending,
		Uploading,
		Completed,
		Failed,
		Expired
	}
}
namespace FollowMePeak.Managers
{
	public class ClimbRecordingManager
	{
		[CompilerGenerated]
		private sealed class <RecordClimbRoutine>d__13 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public ClimbRecordingManager <>4__this;

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

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

			[DebuggerHidden]
			public <RecordClimbRoutine>d__13(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
				//IL_003d: Unknown result type (might be due to invalid IL or missing references)
				int num = <>1__state;
				ClimbRecordingManager climbRecordingManager = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					break;
				case 1:
					<>1__state = -1;
					break;
				}
				if (climbRecordingManager._isRecording)
				{
					if ((Object)(object)Camera.main != (Object)null)
					{
						climbRecordingManager._currentRecordedClimb.Add(((Component)Camera.main).transform.position);
					}
					<>2__current = (object)new WaitForSeconds(2f);
					<>1__state = 1;
					return 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();
			}
		}

		private readonly ClimbDataService _climbDataService;

		private readonly ManualLogSource _logger;

		private List<Vector3> _currentRecordedClimb = new List<Vector3>();

		private bool _isRecording;

		private float _recordingStartTime;

		private MonoBehaviour _coroutineRunner;

		public bool IsRecording => _isRecording;

		public ClimbRecordingManager(ClimbDataService climbDataService, ManualLogSource logger, MonoBehaviour coroutineRunner)
		{
			_climbDataService = climbDataService;
			_logger = logger;
			_coroutineRunner = coroutineRunner;
		}

		public void StartRecording()
		{
			if (!_isRecording)
			{
				_isRecording = true;
				_currentRecordedClimb.Clear();
				_recordingStartTime = Time.time;
				_logger.LogInfo((object)"Kletter-Aufzeichnung gestartet!");
				_coroutineRunner.StartCoroutine(RecordClimbRoutine());
			}
		}

		public void StopRecording()
		{
			if (_isRecording)
			{
				_isRecording = false;
				_logger.LogInfo((object)$"Aufzeichnung gestoppt. {_currentRecordedClimb.Count} Punkte zwischengespeichert.");
			}
		}

		public void SaveCurrentClimb(string biomeName)
		{
			StopRecording();
			if (_currentRecordedClimb.Count >= 2)
			{
				ClimbData climbData = new ClimbData
				{
					Id = Guid.NewGuid(),
					CreationTime = DateTime.Now,
					BiomeName = (biomeName ?? "Unbekannt"),
					DurationInSeconds = Time.time - _recordingStartTime,
					Points = _currentRecordedClimb.Select((Vector3 vec) => new SerializableVector3(vec)).ToList()
				};
				climbData.GenerateShareCode();
				_climbDataService.AddClimb(climbData);
				_climbDataService.SaveClimbsToFile(addNewClimb: false);
				ShowTagSelectionForNewClimb(climbData);
			}
		}

		private void ShowTagSelectionForNewClimb(ClimbData climbData)
		{
			_logger.LogInfo((object)("Neue Kletterroute erstellt: " + climbData.GetDisplayName() + " (Code: " + climbData.ShareCode + ")"));
			Plugin.Instance.ShowTagSelectionForNewClimb(climbData);
		}

		[IteratorStateMachine(typeof(<RecordClimbRoutine>d__13))]
		private IEnumerator RecordClimbRoutine()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <RecordClimbRoutine>d__13(0)
			{
				<>4__this = this
			};
		}
	}
	public class ClimbVisualizationManager
	{
		private readonly ClimbDataService _climbDataService;

		private Dictionary<Guid, bool> _climbVisibility = new Dictionary<Guid, bool>();

		private Dictionary<Guid, GameObject> _climbVisualizerObjects = new Dictionary<Guid, GameObject>();

		public ClimbVisualizationManager(ClimbDataService climbDataService)
		{
			_climbDataService = climbDataService;
		}

		public bool IsClimbVisible(Guid climbId)
		{
			if (_climbVisibility.ContainsKey(climbId))
			{
				return _climbVisibility[climbId];
			}
			return false;
		}

		public void SetClimbVisibility(Guid climbId, bool visible)
		{
			_climbVisibility[climbId] = visible;
			UpdateVisuals();
		}

		public void ToggleClimbVisibility(Guid climbId)
		{
			if (_climbVisibility.ContainsKey(climbId))
			{
				_climbVisibility[climbId] = !_climbVisibility[climbId];
			}
			else
			{
				_climbVisibility[climbId] = true;
			}
			UpdateVisuals();
		}

		public void ShowAllClimbs()
		{
			foreach (ClimbData allClimb in _climbDataService.GetAllClimbs())
			{
				_climbVisibility[allClimb.Id] = true;
			}
			UpdateVisuals();
		}

		public void HideAllClimbs()
		{
			foreach (ClimbData allClimb in _climbDataService.GetAllClimbs())
			{
				_climbVisibility[allClimb.Id] = false;
			}
			UpdateVisuals();
		}

		public void InitializeClimbVisibility()
		{
			_climbVisibility.Clear();
			foreach (ClimbData allClimb in _climbDataService.GetAllClimbs())
			{
				_climbVisibility[allClimb.Id] = false;
			}
		}

		public void UpdateVisuals()
		{
			foreach (GameObject value in _climbVisualizerObjects.Values)
			{
				if ((Object)(object)value != (Object)null)
				{
					Object.Destroy((Object)(object)value);
				}
			}
			_climbVisualizerObjects.Clear();
			foreach (ClimbData allClimb in _climbDataService.GetAllClimbs())
			{
				if (_climbVisibility.ContainsKey(allClimb.Id) && _climbVisibility[allClimb.Id])
				{
					CreateClimbVisualizer(allClimb);
				}
			}
		}

		private void CreateClimbVisualizer(ClimbData climbData)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected O, but got Unknown
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			List<Vector3> list = climbData.Points.Select((SerializableVector3 p) => p.ToVector3()).ToList();
			if (list.Count >= 2)
			{
				GameObject val = new GameObject($"ClimbVisualizer_{climbData.Id}");
				LineRenderer val2 = val.AddComponent<LineRenderer>();
				((Renderer)val2).material = new Material(Shader.Find("Legacy Shaders/Particles/Alpha Blended Premultiply"));
				if (climbData.IsFromCloud)
				{
					val2.startColor = Color.blue;
					val2.endColor = Color.cyan;
				}
				else
				{
					val2.startColor = Color.red;
					val2.endColor = new Color(1f, 0.5f, 0f);
				}
				val2.startWidth = 0.2f;
				val2.endWidth = 0.2f;
				val2.positionCount = list.Count;
				val2.SetPositions(list.ToArray());
				_climbVisualizerObjects[climbData.Id] = val;
			}
		}

		public void ClearVisuals()
		{
			foreach (GameObject value in _climbVisualizerObjects.Values)
			{
				if ((Object)(object)value != (Object)null)
				{
					Object.Destroy((Object)(object)value);
				}
			}
			_climbVisualizerObjects.Clear();
			_climbVisibility.Clear();
		}
	}
}
namespace FollowMePeak.Config
{
	internal static class ApiKeys
	{
		public const string ServerApiKey = "FMP_2024_PEAK_CLIMB_AUTH_v1_7B8C9D2E";

		public static bool IsConfigured => !"FMP_2024_PEAK_CLIMB_AUTH_v1_7B8C9D2E".Contains("PLACEHOLDER");
	}
}