Decompiled source of FollowMe v1.0.3

FollowMePeak.dll

Decompiled 2 weeks ago
using System;
using System.Buffers.Binary;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
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 System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FollowMePeak.Detection;
using FollowMePeak.Managers;
using FollowMePeak.ModMenu;
using FollowMePeak.ModMenu.UI;
using FollowMePeak.ModMenu.UI.Helpers;
using FollowMePeak.ModMenu.UI.Tabs;
using FollowMePeak.ModMenu.UI.Tabs.Components;
using FollowMePeak.Models;
using FollowMePeak.Patches;
using FollowMePeak.Services;
using FollowMePeak.Utils;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
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 PEAK")]
[assembly: AssemblyFileVersion("1.0.3.0")]
[assembly: AssemblyInformationalVersion("1.0.3+9dff2c98d9e55f6a97c82bd3e8aab032bda484a8")]
[assembly: AssemblyProduct("FollowMePeak")]
[assembly: AssemblyTitle("FollowMePeak")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.3.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.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", "1.0.3")]
	public class Plugin : BaseUnityPlugin
	{
		[CompilerGenerated]
		private sealed class <>c__DisplayClass28_0
		{
			public Plugin <>4__this;

			public bool loadComplete;

			public bool loadSuccess;

			internal void <LoadModUIAssetBundle>b__0(bool success)
			{
				<>4__this._modLogger.Info($"[AssetBundle] LoadModUIBundle callback received: success={success}");
				loadComplete = true;
				loadSuccess = success;
			}
		}

		[CompilerGenerated]
		private sealed class <InitializePathSystem>d__29 : 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__29(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 plugin = <>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;
						plugin._climbDataService.CurrentLevelID = $"{((Scene)(ref scene)).name}_{currentLevelIndex}";
						plugin._modLogger.Info("Level erkannt: " + plugin._climbDataService.CurrentLevelID);
						plugin._climbDataService.LoadClimbsFromFile();
						if (plugin._serverConfigService.Config.EnableCloudSync && plugin._serverConfigService.Config.AutoDownload)
						{
							plugin._modLogger.Info("Server-side pagination enabled - data will be loaded per page in UI");
						}
						plugin._visualizationManager.InitializeClimbVisibility();
					}
					else
					{
						plugin._modLogger.Error("NextLevelService or its data could not be found!");
						plugin._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();
			}
		}

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

			private object <>2__current;

			public Plugin <>4__this;

			private <>c__DisplayClass28_0 <>8__1;

			private float <timeout>5__2;

			private float <elapsed>5__3;

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

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

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

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

			private bool MoveNext()
			{
				int num = <>1__state;
				Plugin plugin = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>8__1 = new <>c__DisplayClass28_0();
					<>8__1.<>4__this = <>4__this;
					plugin._modLogger.Info("[AssetBundle] Starting AssetBundle load coroutine...");
					plugin._modLogger.Info("[AssetBundle] Current Directory: " + Directory.GetCurrentDirectory());
					plugin._modLogger.Info("[AssetBundle] BepInEx Plugin Path: " + Paths.PluginPath);
					plugin._modLogger.Info("[AssetBundle] Application.dataPath: " + Application.dataPath);
					<>8__1.loadComplete = false;
					<>8__1.loadSuccess = false;
					plugin._modLogger.Info("[AssetBundle] Calling AssetBundleService.Instance.LoadModUIBundle...");
					<>2__current = AssetBundleService.Instance.LoadModUIBundle(delegate(bool success)
					{
						<>8__1.<>4__this._modLogger.Info($"[AssetBundle] LoadModUIBundle callback received: success={success}");
						<>8__1.loadComplete = true;
						<>8__1.loadSuccess = success;
					});
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					<timeout>5__2 = 5f;
					<elapsed>5__3 = 0f;
					plugin._modLogger.Info($"[AssetBundle] Waiting for load completion (max {<timeout>5__2} seconds)...");
					break;
				case 2:
					<>1__state = -1;
					break;
				}
				if (!<>8__1.loadComplete && <elapsed>5__3 < <timeout>5__2)
				{
					<elapsed>5__3 += Time.deltaTime;
					<>2__current = null;
					<>1__state = 2;
					return true;
				}
				if (!<>8__1.loadComplete)
				{
					plugin._modLogger.Error($"[AssetBundle] Timeout waiting for AssetBundle load after {<timeout>5__2} seconds");
					plugin._modLogger.Error($"[AssetBundle] Service instance exists: {AssetBundleService.Instance != null}");
					plugin._modLogger.Error($"[AssetBundle] Service IsLoaded: {AssetBundleService.Instance?.IsLoaded ?? false}");
				}
				else if (<>8__1.loadSuccess)
				{
					plugin._modLogger.Info("[AssetBundle] Successfully loaded, notifying ModMenuManager");
					plugin._modLogger.Info($"[AssetBundle] ModMenuManager exists: {plugin._modMenuManager != null}");
					plugin._modMenuManager?.OnAssetBundleLoaded();
					plugin._modLogger.Info("[AssetBundle] OnAssetBundleLoaded called");
				}
				else
				{
					plugin._modLogger.Error("[AssetBundle] AssetBundle loading failed - check AssetBundleService logs for details");
				}
				plugin._modLogger.Info($"[AssetBundle] LoadModUIAssetBundle coroutine finished - Success: {<>8__1.loadSuccess}");
				return false;
			}

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

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

		public const string MOD_VERSION = "1.0.3";

		public static ConfigEntry<KeyCode> ModMenuToggleKey;

		public static ConfigEntry<bool> SaveDeathClimbs;

		public static ConfigEntry<LogLevel> LoggingLevel;

		private ModLogger _modLogger;

		private ClimbDataService _climbDataService;

		private ClimbRecordingManager _recordingManager;

		private ClimbVisualizationManager _visualizationManager;

		private ServerConfigService _serverConfigService;

		private VPSApiService _vpsApiService;

		private ClimbUploadService _climbUploadService;

		private ClimbDownloadService _climbDownloadService;

		private ModMenuManager _modMenuManager;

		private Harmony _harmony;

		private bool _gameEndedThisSession;

		public static Plugin Instance { get; private set; }

		public ClimbDataService ClimbDataService => _climbDataService;

		public ClimbRecordingManager GetRecordingManager()
		{
			return _recordingManager;
		}

		private void Awake()
		{
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Expected O, but got Unknown
			Instance = this;
			_modLogger = new ModLogger(((BaseUnityPlugin)this).Logger);
			ModLogger.Instance = _modLogger;
			_modLogger.Info("Plugin " + ((BaseUnityPlugin)this).Info.Metadata.GUID + " loaded!");
			InitializeControlsConfig();
			_modLogger.Info("[FlyDetection] System initialized with fixed configuration");
			_modLogger.Info($"[FlyDetection] Threshold: {50f}, CheckInterval: {0.5f}");
			InitializeServices();
			SceneManager.sceneLoaded += OnSceneLoaded;
			_harmony = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID);
			_harmony.PatchAll(typeof(PluginPatches));
			PlayerDeathPatch.ApplyPatch(_harmony);
			RunManagerPatch.ApplyPatch(_harmony);
			EndGamePatch.ApplyPatch(_harmony);
			_modLogger.Info("Harmony Patches applied.");
		}

		private void InitializeServices()
		{
			_climbDataService = new ClimbDataService(_modLogger);
			_recordingManager = new ClimbRecordingManager(_climbDataService, _modLogger, (MonoBehaviour)(object)this);
			_visualizationManager = new ClimbVisualizationManager(_climbDataService);
			_serverConfigService = new ServerConfigService(_modLogger);
			_vpsApiService = new VPSApiService(_modLogger, _serverConfigService.Config, (MonoBehaviour)(object)this);
			_climbUploadService = new ClimbUploadService(_modLogger, _vpsApiService, _serverConfigService);
			_climbDownloadService = new ClimbDownloadService(_modLogger, _vpsApiService, _serverConfigService, _climbDataService);
			ModMenuManager.ServerConfig = _serverConfigService;
			ModMenuManager.ApiService = _vpsApiService;
			ModMenuManager.UploadService = _climbUploadService;
			ModMenuManager.DownloadService = _climbDownloadService;
			ModMenuManager.ClimbDataService = _climbDataService;
			ModMenuManager.VisualizationManager = _visualizationManager;
			_modMenuManager = new ModMenuManager();
			((MonoBehaviour)this).StartCoroutine(LoadModUIAssetBundle());
			_modLogger.Info("All services initialized successfully");
			if (_serverConfigService.Config.EnableCloudSync)
			{
				_vpsApiService.CheckServerHealth(delegate(bool isHealthy)
				{
					_modLogger.Info("Initial server health check: " + (isHealthy ? "Connected" : "Failed"));
				});
			}
		}

		private void InitializeControlsConfig()
		{
			ModMenuToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "ModMenuToggleKey", (KeyCode)282, "Key to toggle the mod menu");
			SaveDeathClimbs = ((BaseUnityPlugin)this).Config.Bind<bool>("Gameplay", "SaveDeathClimbs", false, "Save climbs where the player died (these will not be uploaded to cloud)");
			LoggingLevel = ((BaseUnityPlugin)this).Config.Bind<LogLevel>("Logging", "LogLevel", LogLevel.Error, "Logging level: None=0, Error=1, Warning=2, Info=3, Debug=4, Verbose=5");
			ModLogger.CurrentLevel = LoggingLevel.Value;
			LoggingLevel.SettingChanged += delegate
			{
				ModLogger.CurrentLevel = LoggingLevel.Value;
				_modLogger.Info($"[Config] LogLevel changed to: {LoggingLevel.Value}");
			};
			FlyDetectionConfig.ValidateConfig();
		}

		private void OnDestroy()
		{
			_modLogger.Info("Plugin " + ((BaseUnityPlugin)this).Info.Metadata.GUID + " unloading...");
			SceneManager.sceneLoaded -= OnSceneLoaded;
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			_modLogger.Info("Harmony patches removed.");
			_modMenuManager?.Cleanup();
			_modMenuManager = null;
			_visualizationManager?.ClearVisuals();
			_visualizationManager = null;
			_recordingManager?.StopRecording();
			_recordingManager = null;
			_climbUploadService = null;
			_climbDownloadService = null;
			_vpsApiService = null;
			_serverConfigService = null;
			_climbDataService = null;
			ModMenuManager.ServerConfig = null;
			ModMenuManager.ApiService = null;
			ModMenuManager.UploadService = null;
			ModMenuManager.DownloadService = null;
			ModMenuManager.ClimbDataService = null;
			ModMenuManager.VisualizationManager = null;
			AssetBundleService.Instance.Unload();
			Instance = null;
			_modLogger.Info("Plugin " + ((BaseUnityPlugin)this).Info.Metadata.GUID + " unloaded!");
		}

		private void Update()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			if (Input.GetKeyDown(ModMenuToggleKey.Value))
			{
				_modLogger.Info($"[Plugin] {ModMenuToggleKey.Value} pressed - Toggling Mod Menu");
				_modMenuManager?.ToggleAssetBundleMenu();
			}
			_modMenuManager?.Update();
			SimpleFlyDetector.PerformDetection();
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			SimpleFlyDetector.OnSceneChanged(((Scene)(ref scene)).name);
			if (((Scene)(ref scene)).name.StartsWith("Level_"))
			{
				if (_gameEndedThisSession)
				{
					_gameEndedThisSession = false;
					_modLogger.Info("[Level] Resetting game ended flag - new level started");
				}
				if (_recordingManager != null && _recordingManager.IsRecording)
				{
					_modLogger.Info("Stopping previous recording due to scene change to " + ((Scene)(ref scene)).name);
					_recordingManager.StopRecording();
				}
				((MonoBehaviour)this).StartCoroutine(InitializePathSystem(scene));
				_modLogger.Info("Level " + ((Scene)(ref scene)).name + " loaded - waiting for RUN STARTED event");
			}
			else
			{
				if (_recordingManager != null && _recordingManager.IsRecording)
				{
					_modLogger.Info("Stopping recording due to leaving level (new scene: " + ((Scene)(ref scene)).name);
					_recordingManager.StopRecording();
				}
				_climbDataService.CurrentLevelID = "";
				_visualizationManager.ClearVisuals();
			}
		}

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

		[IteratorStateMachine(typeof(<InitializePathSystem>d__29))]
		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__29(0)
			{
				<>4__this = this,
				scene = scene
			};
		}

		public void OnRunStartedFromPatch()
		{
			if (_gameEndedThisSession)
			{
				_modLogger.Info("[RunManager] Ignoring RUN STARTED after helicopter ending");
				return;
			}
			_modLogger.Info("[RunManager] RUN STARTED - Activating fly detection and climb recording");
			SimpleFlyDetector.OnRunStarted();
			if (_recordingManager != null)
			{
				_recordingManager.StartRecording();
			}
			else
			{
				_modLogger.Error("RecordingManager is null when trying to start recording!");
			}
		}

		public void OnHelicopterEnding()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if (ClimbRecordingManager.PlayerDiedThisSession)
			{
				_modLogger.Info("[Helicopter] Ignoring helicopter ending - player already died this session");
				return;
			}
			_modLogger.Info("[Helicopter] Game ending detected - saving Peak climb");
			Scene activeScene = SceneManager.GetActiveScene();
			string name = ((Scene)(ref activeScene)).name;
			_modLogger.Info("[Helicopter] Current scene: " + name);
			if (_recordingManager != null && _recordingManager.IsRecording)
			{
				_modLogger.Info("[Helicopter] Recording active, saving as Peak");
				_recordingManager.SaveCurrentClimb("Peak");
				_modLogger.Info("[Helicopter] Final Peak climb saved");
			}
			else if (_recordingManager != null)
			{
				_modLogger.Warning("[Helicopter] No active recording to save for Peak");
			}
			else
			{
				_modLogger.Error("[Helicopter] RecordingManager is null!");
			}
			SimpleFlyDetector.OnSceneChanged("GameEnded");
			_gameEndedThisSession = true;
			_modLogger.Info("[Helicopter] Recording disabled until next level load");
		}

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

		public void ShowTagSelectionForNewClimb(ClimbData climbData)
		{
			UploadIfAutoUploadEnabled(climbData);
		}

		private void UploadIfAutoUploadEnabled(ClimbData climbData)
		{
			if (climbData.WasDeathClimb)
			{
				_modLogger.Info($"[Death] Death climb {climbData.Id} will not be uploaded to cloud");
			}
			else if (_serverConfigService.Config.EnableCloudSync && _serverConfigService.Config.AutoUpload)
			{
				_climbUploadService.QueueForUpload(climbData, _climbDataService.CurrentLevelID);
				_modLogger.Info($"Queued climb for upload: {climbData.Id}");
			}
		}
	}
}
namespace FollowMePeak.Utils
{
	public static class ClimbDataCrusher
	{
		private static class NanCodes
		{
			internal const byte AbsoluteReposition = 1;

			internal const byte AsHalfFloats = 2;
		}

		public static void WriteClimbData(MemoryStream dataStream, ClimbData climb)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			if (climb.Points.Count < 2)
			{
				return;
			}
			byte[] array = new byte[14];
			Vector3 val = climb.Points[0];
			EncodePointAsFloats(array, val);
			dataStream.Write(array.AsSpan(0, 12));
			foreach (Vector3 item in climb.Points.Skip(1))
			{
				QuarterFloat quarter = QuarterFloat.FromFloat(item.x - val.x);
				QuarterFloat quarter2 = QuarterFloat.FromFloat(item.y - val.y);
				QuarterFloat quarter3 = QuarterFloat.FromFloat(item.z - val.z);
				if (quarter.IsInfinity() || quarter2.IsInfinity() || quarter3.IsInfinity())
				{
					HalfFloat half = HalfFloat.FromFloat(item.x - val.x);
					HalfFloat half2 = HalfFloat.FromFloat(item.y - val.y);
					HalfFloat half3 = HalfFloat.FromFloat(item.z - val.z);
					if (half.IsInfinity() || half2.IsInfinity() || half3.IsInfinity())
					{
						EncodeQuarterFloat(array.AsSpan(0, 1), QuarterFloat.EncodeNaN(1));
						EncodePointAsFloats(array.AsSpan(1, 12), item);
						val = item;
						dataStream.Write(array.AsSpan(0, 13));
						continue;
					}
					EncodeQuarterFloat(array.AsSpan(0, 1), QuarterFloat.EncodeNaN(2));
					EncodeHalfFloat(array.AsSpan(1, 2), half);
					EncodeHalfFloat(array.AsSpan(3, 2), half2);
					EncodeHalfFloat(array.AsSpan(5, 2), half3);
					val.x += half.ToFloat();
					val.y += half2.ToFloat();
					val.z += half3.ToFloat();
					dataStream.Write(array.AsSpan(0, 7));
				}
				else if (!quarter.IsZero() || !quarter2.IsZero() || !quarter3.IsZero())
				{
					EncodeQuarterFloat(array.AsSpan(0, 1), quarter);
					EncodeQuarterFloat(array.AsSpan(1, 1), quarter2);
					EncodeQuarterFloat(array.AsSpan(2, 1), quarter3);
					val.x += quarter.ToFloat();
					val.y += quarter2.ToFloat();
					val.z += quarter3.ToFloat();
					dataStream.Write(array.AsSpan(0, 3));
				}
			}
		}

		public static void ReadClimbData(byte[] data, ClimbData climb)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			climb.Points = new List<Vector3>();
			if (data == null || data.Length < 12)
			{
				return;
			}
			Vector3 item = DecodePointFromFloats(data.AsSpan(0, 12));
			climb.Points.Add(item);
			int num = 12;
			while (num < data.Length)
			{
				QuarterFloat quarterFloat = DecodeQuarterFloat(data.AsSpan(num, 1));
				if (quarterFloat.IsNaN())
				{
					switch (quarterFloat.DecodeNaN())
					{
					case 1:
						item = DecodePointFromFloats(data.AsSpan(num + 1, 12));
						climb.Points.Add(item);
						num += 13;
						break;
					case 2:
						item.x += DecodeHalfFloat(data.AsSpan(num + 1, 2)).ToFloat();
						item.y += DecodeHalfFloat(data.AsSpan(num + 3, 2)).ToFloat();
						item.z += DecodeHalfFloat(data.AsSpan(num + 5, 2)).ToFloat();
						climb.Points.Add(item);
						num += 7;
						break;
					default:
						throw new InvalidOperationException($"Unexpected NaN code in climb data: {quarterFloat.DecodeNaN()}");
					}
				}
				else
				{
					item.x += quarterFloat.ToFloat();
					item.y += DecodeQuarterFloat(data.AsSpan(num + 1, 1)).ToFloat();
					item.z += DecodeQuarterFloat(data.AsSpan(num + 2, 1)).ToFloat();
					climb.Points.Add(item);
					num += 3;
				}
			}
		}

		private static void EncodePointAsFloats(Span<byte> span, Vector3 firstPoint)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			EncodeFloat(span.Slice(0, 4), firstPoint.x);
			EncodeFloat(span.Slice(4, 4), firstPoint.y);
			EncodeFloat(span.Slice(8, 4), firstPoint.z);
		}

		private static Vector3 DecodePointFromFloats(Span<byte> data)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			return new Vector3(DecodeFloat(data.Slice(0, 4)), DecodeFloat(data.Slice(4, 4)), DecodeFloat(data.Slice(8, 4)));
		}

		private static void EncodeFloat(Span<byte> span, float value)
		{
			BinaryPrimitives.WriteInt32LittleEndian(span, BitConverter.SingleToInt32Bits(value));
		}

		private static float DecodeFloat(Span<byte> span)
		{
			return BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(span));
		}

		private static void EncodeHalfFloat(Span<byte> span, HalfFloat half)
		{
			BinaryPrimitives.WriteUInt16LittleEndian(span, half.RawValue);
		}

		private static HalfFloat DecodeHalfFloat(Span<byte> span)
		{
			HalfFloat result = default(HalfFloat);
			result.RawValue = BinaryPrimitives.ReadUInt16LittleEndian(span);
			return result;
		}

		private static void EncodeQuarterFloat(Span<byte> span, QuarterFloat quarter)
		{
			span[0] = quarter.RawValue;
		}

		private static QuarterFloat DecodeQuarterFloat(Span<byte> span)
		{
			QuarterFloat result = default(QuarterFloat);
			result.RawValue = span[0];
			return result;
		}
	}
	public static class CommonJsonSettings
	{
		public class ApiVector3Converter : JsonConverter<Vector3>
		{
			public override void WriteJson(JsonWriter writer, Vector3 value, JsonSerializer serializer)
			{
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_0029: Unknown result type (might be due to invalid IL or missing references)
				//IL_0040: Unknown result type (might be due to invalid IL or missing references)
				writer.WriteStartObject();
				writer.WritePropertyName("x");
				writer.WriteValue(value.x);
				writer.WritePropertyName("y");
				writer.WriteValue(value.y);
				writer.WritePropertyName("z");
				writer.WriteValue(value.z);
				writer.WriteEndObject();
			}

			public override Vector3 ReadJson(JsonReader reader, Type objectType, Vector3 existingValue, bool hasExistingValue, JsonSerializer serializer)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0001: 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_000f: Invalid comparison between Unknown and I4
				//IL_00cc: 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)
				//IL_001b: Invalid comparison between Unknown and I4
				Vector3 result = existingValue;
				while (reader.Read() && (int)reader.TokenType != 13)
				{
					if ((int)reader.TokenType != 4)
					{
						continue;
					}
					string text = (string)reader.Value;
					if (reader.Read())
					{
						switch (text)
						{
						case "x":
						case "X":
							result.x = Convert.ToSingle(reader.Value);
							break;
						case "y":
						case "Y":
							result.y = Convert.ToSingle(reader.Value);
							break;
						case "z":
						case "Z":
							result.z = Convert.ToSingle(reader.Value);
							break;
						}
					}
				}
				return result;
			}
		}

		public static readonly JsonSerializerSettings Default = new JsonSerializerSettings
		{
			Formatting = (Formatting)1,
			Converters = new List<JsonConverter>(1) { (JsonConverter)(object)new ApiVector3Converter() }
		};

		public static readonly JsonSerializerSettings Compact = new JsonSerializerSettings
		{
			Formatting = (Formatting)0,
			NullValueHandling = (NullValueHandling)0,
			DefaultValueHandling = (DefaultValueHandling)0,
			Converters = new List<JsonConverter>(1) { (JsonConverter)(object)new ApiVector3Converter() }
		};
	}
	public struct HalfFloat
	{
		public const byte TotalNumBits = 16;

		public const byte NumExponentBits = 4;

		public const byte NumMantissaBits = 11;

		public const byte SignBitPos = 15;

		private const ushort SignBit = 32768;

		private const ushort ExponentMask = 30720;

		private const ushort MantissaMask = 2047;

		private const ushort ExponentInfinity = 15;

		private const int ExponentBias = 7;

		private const int SingleExponentBias = 127;

		private const byte SingleNumExponentBits = 8;

		private const byte SingleNumMantissaBits = 23;

		private const byte SingleSignBitPos = 31;

		private const uint SingleExponentMask = 2139095040u;

		private const uint SingleMantissaMask = 8388607u;

		private const uint SingleExponentInfinity = 255u;

		public ushort RawValue;

		public static HalfFloat NaN
		{
			get
			{
				HalfFloat result = default(HalfFloat);
				result.RawValue = 30721;
				return result;
			}
		}

		public ushort ExponentBits => (ushort)((uint)(RawValue & 0x7800) >> 11);

		public ushort MantissaBits => (ushort)(RawValue & 0x7FFu);

		public static HalfFloat FromFloat(float value)
		{
			int num = BitConverter.SingleToInt32Bits(value);
			uint num2 = (uint)num >> 31;
			uint num3 = (uint)(num & 0x7F800000) >> 23;
			uint num4 = (uint)num & 0x7FFFFFu;
			int num5 = (int)(num3 - 127 + 7);
			if (num5 == -11)
			{
				num4 = 0u;
				num5++;
			}
			else if (num5 > -11)
			{
				uint num6 = (uint)(1 << 11 + Math.Max(0, 1 - num5));
				if ((num4 & num6) != 0)
				{
					num4 += num6;
					if ((num4 & 0xFF800000u) != 0)
					{
						num4 = 0u;
						num5++;
					}
				}
			}
			if (num5 >= 15)
			{
				if (num4 != 0 && num3 == 255)
				{
					return NaN;
				}
				num5 = 15;
				num4 = 0u;
			}
			else if (num5 <= 0)
			{
				if (num5 > -11)
				{
					num4 |= 0x800000u;
					int num7 = 1 - num5;
					num4 >>= num7;
					num5 = 0;
				}
				else
				{
					num5 = 0;
					num4 = 0u;
				}
			}
			ushort num8 = (ushort)(num2 << 15);
			ushort num9 = (ushort)(num5 << 11);
			ushort num10 = (ushort)(num4 >> 12);
			HalfFloat result = default(HalfFloat);
			result.RawValue = (ushort)(num8 | num9 | num10);
			return result;
		}

		public float ToFloat()
		{
			uint num = (uint)((RawValue & 0x8000) << 16);
			uint exponentBits = ExponentBits;
			uint num2 = MantissaBits;
			if (exponentBits == 0 && num2 == 0)
			{
				return BitConverter.Int32BitsToSingle((int)num);
			}
			uint num3 = exponentBits + 127 - 7;
			if (exponentBits == 15)
			{
				num3 = 255u;
			}
			else if (exponentBits == 0)
			{
				while ((num2 & 0x800) == 0)
				{
					num2 <<= 1;
					num3--;
				}
				num2 &= 0x7FFu;
				num3++;
			}
			return BitConverter.Int32BitsToSingle((int)(num | (num3 << 23) | (num2 << 12)));
		}

		public static HalfFloat EncodeNaN(ushort value)
		{
			value++;
			ushort num = (ushort)((uint)value >> 11);
			if (num > 1)
			{
				throw new ArgumentOutOfRangeException("value", "Value too large to encode as NaN");
			}
			ushort num2 = (ushort)(value & 0x7FFu);
			HalfFloat result = default(HalfFloat);
			result.RawValue = (ushort)((uint)(num << 15) | 0x7800u | num2);
			return result;
		}

		public ushort DecodeNaN()
		{
			if (!IsNaN())
			{
				throw new InvalidOperationException("Not a NaN value");
			}
			ushort num = (ushort)((RawValue & 0x8000) >>> 15);
			ushort num2 = (ushort)(RawValue & 0x7FFu);
			return (ushort)(((num << 11) | num2) - 1);
		}

		public bool IsNaN()
		{
			if (ExponentBits == 15)
			{
				return MantissaBits != 0;
			}
			return false;
		}

		public bool IsInfinity()
		{
			if (ExponentBits == 15)
			{
				return MantissaBits == 0;
			}
			return false;
		}

		public bool IsPositiveInfinity()
		{
			if (IsInfinity())
			{
				return (RawValue & 0x8000) == 0;
			}
			return false;
		}

		public bool IsNegativeInfinity()
		{
			if (IsInfinity())
			{
				return (RawValue & 0x8000) != 0;
			}
			return false;
		}

		public bool IsZero()
		{
			return (RawValue & -32769) == 0;
		}

		public bool IsPositiveZero()
		{
			return RawValue == 0;
		}

		public bool IsNegativeZero()
		{
			return RawValue == 32768;
		}

		public bool IsDenormalized()
		{
			if (ExponentBits == 0)
			{
				return MantissaBits != 0;
			}
			return false;
		}

		public override string ToString()
		{
			return ToFloat().ToString(CultureInfo.InvariantCulture);
		}
	}
	public struct QuarterFloat
	{
		public const byte TotalNumBits = 8;

		public const byte NumExponentBits = 2;

		public const byte NumMantissaBits = 5;

		public const byte SignBitPos = 7;

		private const byte SignBit = 128;

		private const byte ExponentMask = 96;

		private const byte MantissaMask = 31;

		private const byte ExponentInfinity = 3;

		private const int ExponentBias = 1;

		private const int SingleExponentBias = 127;

		private const byte SingleNumExponentBits = 8;

		private const byte SingleNumMantissaBits = 23;

		private const byte SingleSignBitPos = 31;

		private const uint SingleExponentMask = 2139095040u;

		private const uint SingleMantissaMask = 8388607u;

		private const uint SingleExponentInfinity = 255u;

		public byte RawValue;

		public static QuarterFloat NaN
		{
			get
			{
				QuarterFloat result = default(QuarterFloat);
				result.RawValue = 97;
				return result;
			}
		}

		public byte ExponentBits => (byte)((uint)(RawValue & 0x60) >> 5);

		public byte MantissaBits => (byte)(RawValue & 0x1Fu);

		public static QuarterFloat FromFloat(float value)
		{
			int num = BitConverter.SingleToInt32Bits(value);
			uint num2 = (uint)num >> 31;
			uint num3 = (uint)(num & 0x7F800000) >> 23;
			uint num4 = (uint)num & 0x7FFFFFu;
			int num5 = (int)(num3 - 127 + 1);
			if (num5 == -5)
			{
				num4 = 0u;
				num5++;
			}
			else if (num5 > -5)
			{
				uint num6 = (uint)(1 << 17 + Math.Max(0, 1 - num5));
				if ((num4 & num6) != 0)
				{
					num4 += num6;
					if ((num4 & 0xFF800000u) != 0)
					{
						num4 = 0u;
						num5++;
					}
				}
			}
			if (num5 >= 3)
			{
				if (num4 != 0 && num3 == 255)
				{
					return NaN;
				}
				num5 = 3;
				num4 = 0u;
			}
			else if (num5 <= 0)
			{
				if (num5 > -5)
				{
					num4 |= 0x800000u;
					int num7 = 1 - num5;
					num4 >>= num7;
					num5 = 0;
				}
				else
				{
					num5 = 0;
					num4 = 0u;
				}
			}
			byte b = (byte)(num2 << 7);
			byte b2 = (byte)(num5 << 5);
			byte b3 = (byte)(num4 >> 18);
			QuarterFloat result = default(QuarterFloat);
			result.RawValue = (byte)(b | b2 | b3);
			return result;
		}

		public float ToFloat()
		{
			uint num = (uint)((RawValue & 0x80) << 24);
			uint exponentBits = ExponentBits;
			uint num2 = MantissaBits;
			if (exponentBits == 0 && num2 == 0)
			{
				return BitConverter.Int32BitsToSingle((int)num);
			}
			uint num3 = exponentBits + 127 - 1;
			if (exponentBits == 3)
			{
				num3 = 255u;
			}
			else if (exponentBits == 0)
			{
				while ((num2 & 0x20) == 0)
				{
					num2 <<= 1;
					num3--;
				}
				num2 &= 0x1Fu;
				num3++;
			}
			return BitConverter.Int32BitsToSingle((int)(num | (num3 << 23) | (num2 << 18)));
		}

		public static QuarterFloat EncodeNaN(byte value)
		{
			value++;
			byte b = (byte)((uint)value >> 5);
			if (b > 1)
			{
				throw new ArgumentOutOfRangeException("value", "Value too large to encode as NaN");
			}
			byte b2 = (byte)(value & 0x1Fu);
			QuarterFloat result = default(QuarterFloat);
			result.RawValue = (byte)((uint)(b << 7) | 0x60u | b2);
			return result;
		}

		public byte DecodeNaN()
		{
			if (!IsNaN())
			{
				throw new InvalidOperationException("Not a NaN value");
			}
			byte num = (byte)((RawValue & 0x80) >>> 7);
			byte b = (byte)(RawValue & 0x1Fu);
			return (byte)(((num << 5) | b) - 1);
		}

		public bool IsNaN()
		{
			if (ExponentBits == 3)
			{
				return MantissaBits != 0;
			}
			return false;
		}

		public bool IsInfinity()
		{
			if (ExponentBits == 3)
			{
				return MantissaBits == 0;
			}
			return false;
		}

		public bool IsPositiveInfinity()
		{
			if (IsInfinity())
			{
				return (RawValue & 0x80) == 0;
			}
			return false;
		}

		public bool IsNegativeInfinity()
		{
			if (IsInfinity())
			{
				return (RawValue & 0x80) != 0;
			}
			return false;
		}

		public bool IsZero()
		{
			return (RawValue & -129) == 0;
		}

		public bool IsPositiveZero()
		{
			return RawValue == 0;
		}

		public bool IsNegativeZero()
		{
			return RawValue == 128;
		}

		public bool IsDenormalized()
		{
			if (ExponentBits == 0)
			{
				return MantissaBits != 0;
			}
			return false;
		}

		public override string ToString()
		{
			return ToFloat().ToString(CultureInfo.InvariantCulture);
		}
	}
	public static class FileUtils
	{
		public static Task WriteJsonFileInBackground(ModLogger logger, string filePath, object payload)
		{
			return Task.Run((Func<Task?>)SerializeAndSave);
			async Task SerializeAndSave()
			{
				try
				{
					string json = JsonConvert.SerializeObject(payload, (Formatting)1);
					await WriteFileAtomically(filePath, json);
				}
				catch (Exception ex)
				{
					Exception ex2 = ex;
					Exception e = ex2;
					ThreadingHelper.Instance.StartSyncInvoke((Action)delegate
					{
						logger.Error("Failed to save " + filePath + ": " + e.Message);
					});
				}
			}
		}

		private static async Task WriteFileAtomically(string filePath, string json)
		{
			Directory.CreateDirectory(Path.GetDirectoryName(filePath));
			string newFileName = filePath + ".new";
			await File.WriteAllTextAsync(newFileName, json);
			ReplaceFileAtomically(newFileName, filePath);
		}

		private static void ReplaceFileAtomically(string tempFileName, string targetFileName)
		{
			try
			{
				File.Move(tempFileName, targetFileName);
			}
			catch (IOException)
			{
				File.Replace(tempFileName, targetFileName, null);
			}
		}
	}
	public static class InputValidator
	{
		private static readonly Regex LevelIdPattern = new Regex("^[a-zA-Z0-9_\\-\\.]{1,100}$");

		private static readonly Regex SpecialCharacterPattern = new Regex("[^a-zA-Z0-9_\\-\\s]");

		private static readonly Regex ValidPeakCodePattern = new Regex("^[A-Z0-9]{8}$");

		private static readonly Regex InvalidPeakCodeCharacters = new Regex("[^A-Z0-9]");

		public static string SanitizePlayerName(string input)
		{
			if (string.IsNullOrWhiteSpace(input))
			{
				return "Anonymous";
			}
			string text = SpecialCharacterPattern.Replace(input, "");
			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 = SpecialCharacterPattern.Replace(input, "");
			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 LevelIdPattern.IsMatch(levelId);
		}

		public static bool IsValidPeakCode(string peakCode)
		{
			if (string.IsNullOrWhiteSpace(peakCode))
			{
				return false;
			}
			return ValidPeakCodePattern.IsMatch(peakCode);
		}

		public static string SanitizePeakCode(string input)
		{
			if (string.IsNullOrWhiteSpace(input))
			{
				return "";
			}
			string text = InvalidPeakCodeCharacters.Replace(input.ToUpper(), "");
			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;
		}

		public static int ClampAscentLevel(int ascentLevel)
		{
			return Math.Max(-1, Math.Min(8, ascentLevel));
		}

		public static bool IsValidAscentLevel(int ascentLevel)
		{
			if (ascentLevel >= -1)
			{
				return ascentLevel <= 8;
			}
			return false;
		}
	}
	public enum LogLevel
	{
		None,
		Error,
		Warning,
		Info,
		Debug,
		Verbose
	}
	public class ModLogger
	{
		private readonly ManualLogSource _logger;

		private static LogLevel _currentLevel = LogLevel.Error;

		public static ModLogger Instance { get; set; }

		public static LogLevel CurrentLevel
		{
			get
			{
				return _currentLevel;
			}
			set
			{
				_currentLevel = value;
			}
		}

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

		public void Error(string message)
		{
			if (_currentLevel >= LogLevel.Error)
			{
				_logger.LogError((object)message);
			}
		}

		public void Warning(string message)
		{
			if (_currentLevel >= LogLevel.Warning)
			{
				_logger.LogWarning((object)message);
			}
		}

		public void Info(string message)
		{
			if (_currentLevel >= LogLevel.Info)
			{
				_logger.LogInfo((object)message);
			}
		}

		public void Debug(string message)
		{
			if (_currentLevel >= LogLevel.Debug)
			{
				_logger.LogInfo((object)("[DEBUG] " + message));
			}
		}

		public void Verbose(string message)
		{
			if (_currentLevel >= LogLevel.Verbose)
			{
				_logger.LogInfo((object)("[VERBOSE] " + message));
			}
		}
	}
}
namespace FollowMePeak.Services
{
	public class AssetBundleService
	{
		[CompilerGenerated]
		private sealed class <LoadModUIBundle>d__11 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public AssetBundleService <>4__this;

			public Action<bool> onComplete;

			private AssetBundleCreateRequest <bundleLoadRequest>5__2;

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

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

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

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

			private bool MoveNext()
			{
				int num = <>1__state;
				AssetBundleService assetBundleService = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
				{
					<>1__state = -1;
					if (assetBundleService._isLoaded)
					{
						ModLogger.Instance?.Info("Mod UI bundle is already loaded.");
						onComplete?.Invoke(obj: true);
						return false;
					}
					if (assetBundleService._isLoading)
					{
						ModLogger.Instance?.Warning("Mod UI bundle is already being loaded.");
						return false;
					}
					assetBundleService._isLoading = true;
					byte[] array2 = null;
					try
					{
						Assembly executingAssembly = Assembly.GetExecutingAssembly();
						string text2 = "FollowMePeak.modui";
						using Stream stream = executingAssembly.GetManifestResourceStream(text2);
						if (stream == null)
						{
							ModLogger.Instance?.Error("Embedded resource '" + text2 + "' not found! Make sure the Build Action is set to 'Embedded Resource'.");
							ModLogger.Instance?.Info("Available embedded resources:");
							string[] array = executingAssembly.GetManifestResourceNames();
							foreach (string text3 in array)
							{
								ModLogger.Instance?.Info(" -> " + text3);
							}
							assetBundleService._isLoading = false;
							onComplete?.Invoke(obj: false);
							return false;
						}
						using MemoryStream memoryStream = new MemoryStream();
						stream.CopyTo(memoryStream);
						array2 = memoryStream.ToArray();
					}
					catch (Exception arg)
					{
						ModLogger.Instance?.Error($"An error occurred while reading the embedded asset bundle: {arg}");
						assetBundleService._isLoading = false;
						onComplete?.Invoke(obj: false);
						return false;
					}
					<bundleLoadRequest>5__2 = AssetBundle.LoadFromMemoryAsync(array2);
					<>2__current = <bundleLoadRequest>5__2;
					<>1__state = 1;
					return true;
				}
				case 1:
				{
					<>1__state = -1;
					if ((Object)(object)<bundleLoadRequest>5__2.assetBundle == (Object)null)
					{
						ModLogger.Instance?.Error("Failed to load AssetBundle from memory. The bundle might be corrupt or incompatible.");
						assetBundleService._isLoading = false;
						onComplete?.Invoke(obj: false);
						return false;
					}
					assetBundleService._modUIBundle = <bundleLoadRequest>5__2.assetBundle;
					ModLogger.Instance?.Info("Successfully loaded AssetBundle '" + ((Object)assetBundleService._modUIBundle).name + "' from embedded resource.");
					string[] allAssetNames = assetBundleService._modUIBundle.GetAllAssetNames();
					ModLogger.Instance?.Info($"AssetBundle contains {allAssetNames.Length} assets:");
					string[] array = allAssetNames;
					foreach (string text in array)
					{
						ModLogger.Instance?.Info("  - " + text);
					}
					assetBundleService._isLoaded = true;
					assetBundleService._isLoading = false;
					onComplete?.Invoke(obj: 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 static AssetBundleService _instance;

		private AssetBundle _modUIBundle;

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

		private bool _isLoaded;

		private bool _isLoading;

		public const string MOD_MENU_CANVAS_PREFAB = "ModMenuCanvas";

		public const string MOD_MENU_PANEL_PREFAB = "MyModMenuPanel";

		public const string MOD_MENU_MAIN_PREFAB = "ModMenuMain";

		public static AssetBundleService Instance
		{
			get
			{
				if (_instance == null)
				{
					_instance = new AssetBundleService();
				}
				return _instance;
			}
		}

		public bool IsLoaded => _isLoaded;

		private AssetBundleService()
		{
		}

		[IteratorStateMachine(typeof(<LoadModUIBundle>d__11))]
		public IEnumerator LoadModUIBundle(Action<bool> onComplete = null)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <LoadModUIBundle>d__11(0)
			{
				<>4__this = this,
				onComplete = onComplete
			};
		}

		public GameObject GetPrefab(string prefabName)
		{
			if (!_isLoaded || (Object)(object)_modUIBundle == (Object)null)
			{
				ModLogger.Instance?.Error("AssetBundle is not loaded. Call LoadModUIBundle first.");
				return null;
			}
			if (_cachedAssets.TryGetValue(prefabName, out var value))
			{
				ModLogger.Instance?.Info("Returning cached prefab: " + prefabName);
				return (GameObject)(object)((value is GameObject) ? value : null);
			}
			try
			{
				ModLogger.Instance?.Info("Attempting to load prefab: " + prefabName);
				GameObject val = null;
				val = _modUIBundle.LoadAsset<GameObject>(prefabName);
				if ((Object)(object)val == (Object)null)
				{
					string text = prefabName.ToLower();
					ModLogger.Instance?.Info("Trying lowercase variant: " + text);
					val = _modUIBundle.LoadAsset<GameObject>(text);
				}
				if ((Object)(object)val == (Object)null)
				{
					ModLogger.Instance?.Info("Searching through all assets for partial match...");
					string[] allAssetNames = _modUIBundle.GetAllAssetNames();
					foreach (string text2 in allAssetNames)
					{
						if (text2.ToLower().Contains(prefabName.ToLower()))
						{
							ModLogger.Instance?.Info("Found potential match: " + text2);
							Object val2 = _modUIBundle.LoadAsset(text2);
							if (val2 is GameObject)
							{
								val = (GameObject)(object)((val2 is GameObject) ? val2 : null);
								ModLogger.Instance?.Info("Successfully loaded from path: " + text2);
								break;
							}
						}
					}
				}
				if ((Object)(object)val != (Object)null)
				{
					_cachedAssets[prefabName] = (Object)(object)val;
					ModLogger.Instance?.Info("Successfully loaded and cached prefab: " + prefabName);
					ModLogger.Instance?.Info("Prefab components:");
					Component[] components = val.GetComponents<Component>();
					foreach (Component val3 in components)
					{
						ModLogger.Instance?.Info("  - " + ((object)val3).GetType().Name);
					}
				}
				else
				{
					ModLogger.Instance?.Warning("Prefab '" + prefabName + "' not found in the AssetBundle.");
					ModLogger.Instance?.Info("Available GameObjects in bundle:");
					string[] allAssetNames = _modUIBundle.GetAllAssetNames();
					foreach (string text3 in allAssetNames)
					{
						if (_modUIBundle.LoadAsset(text3) is GameObject)
						{
							ModLogger.Instance?.Info("  - " + text3 + " (GameObject)");
						}
					}
				}
				return val;
			}
			catch (Exception ex)
			{
				ModLogger.Instance?.Error("Error loading prefab '" + prefabName + "': " + ex.Message);
				return null;
			}
		}

		public T GetAsset<T>(string assetName) where T : Object
		{
			if (!_isLoaded || (Object)(object)_modUIBundle == (Object)null)
			{
				ModLogger.Instance?.Error("AssetBundle is not loaded. Call LoadModUIBundle first.");
				return default(T);
			}
			if (_cachedAssets.TryGetValue(assetName, out var value))
			{
				return (T)(object)((value is T) ? value : null);
			}
			try
			{
				T val = _modUIBundle.LoadAsset<T>(assetName);
				if ((Object)(object)val != (Object)null)
				{
					_cachedAssets[assetName] = (Object)(object)val;
				}
				else
				{
					ModLogger.Instance?.Warning("Asset '" + assetName + "' of type " + typeof(T).Name + " not found in AssetBundle");
				}
				return val;
			}
			catch (Exception ex)
			{
				ModLogger.Instance?.Error("Error loading asset '" + assetName + "': " + ex.Message);
				return default(T);
			}
		}

		public void Unload()
		{
			if ((Object)(object)_modUIBundle != (Object)null)
			{
				ModLogger.Instance?.Info("Unloading Mod UI AssetBundle");
				_modUIBundle.Unload(true);
				_modUIBundle = null;
			}
			_cachedAssets.Clear();
			_isLoaded = false;
			_isLoading = false;
		}
	}
	public class ClimbDataService
	{
		private readonly ModLogger _logger;

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

		private string _currentLevelID = "";

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

		public ClimbDataService(ModLogger 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"))
			{
				List<ClimbData> payload = new List<ClimbData>(_allLoadedClimbs);
				string filePath = Path.Combine(Paths.PluginPath, "FollowMePeak_Data", _currentLevelID + ".json");
				FileUtils.WriteJsonFileInBackground(_logger, filePath, payload);
			}
		}

		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.Info("No climb file found for '" + _currentLevelID + "'.");
				return;
			}
			try
			{
				string text = File.ReadAllText(path);
				_allLoadedClimbs = JsonConvert.DeserializeObject<List<ClimbData>>(text, CommonJsonSettings.Default) ?? new List<ClimbData>();
				_logger.Info($"{_allLoadedClimbs.Count} climbs loaded for level '{_currentLevelID}'.");
			}
			catch (Exception ex)
			{
				_logger.Error("Error loading climbs (possibly old format?): " + ex.Message);
			}
		}

		public void ClearClimbs()
		{
			_allLoadedClimbs.Clear();
		}
	}
	public class ClimbDownloadService
	{
		private readonly ModLogger _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(ModLogger logger, VPSApiService apiService, ServerConfigService configService, ClimbDataService climbDataService)
		{
			_logger = logger;
			_apiService = apiService;
			_configService = configService;
			_climbDataService = climbDataService;
		}

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

		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.Debug($"Skipping similar climb: {downloadedClimb.Id}");
						continue;
					}
					_climbDataService.AddClimb(downloadedClimb);
					num++;
					_logger.Debug($"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_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: 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();
					Vector3 val2 = newClimb.Points.Last();
					Vector3 val3 = existingClimb.Points.First();
					Vector3 val4 = existingClimb.Points.Last();
					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, ClimbListMeta> callback = null)
		{
			if (!_configService.Config.EnableCloudSync)
			{
				callback?.Invoke(0, "Cloud sync disabled", null);
				return;
			}
			if (IsDownloading)
			{
				callback?.Invoke(0, "Download in progress", null);
				return;
			}
			IsDownloading = true;
			_logger.Info("Downloading recent climbs from all levels");
			_apiService.DownloadClimbs("recent", delegate(List<ClimbData> recentClimbs, string error, ClimbListMeta meta)
			{
				IsDownloading = false;
				_lastDownload = DateTime.Now;
				if (error != null)
				{
					_logger.Error("Failed to download recent climbs: " + error);
					callback?.Invoke(0, error, null);
					return;
				}
				try
				{
					int num = MergeDownloadedClimbs(recentClimbs, "all_levels");
					_logger.Info($"Downloaded and merged {num} recent climbs");
					callback?.Invoke(num, null, meta);
				}
				catch (Exception ex)
				{
					_logger.Error("Failed to merge recent climbs: " + ex.Message);
					callback?.Invoke(0, ex.Message, null);
				}
			});
		}

		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, ClimbListMeta meta)
			{
				if (error == null && count > 0)
				{
					_logger.Info($"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.Info("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 ModLogger _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(ModLogger 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 (climbData.WasDeathClimb)
			{
				_logger.Info("[Death] Death climb will not be uploaded to cloud");
				return;
			}
			if (climbData.WasFlagged)
			{
				_logger.Warning("[FlyDetection] Climb was flagged during recording - will upload as private!");
				_logger.Warning($"[FlyDetection] Score: {climbData.FlaggedScore}/100");
				_logger.Warning("[FlyDetection] Reason: " + climbData.FlaggedReason);
				_logger.Warning("[FlyDetection] Climb will be uploaded as private due to fly mod detection");
			}
			if (!_configService.Config.EnableCloudSync || !_configService.Config.AutoUpload)
			{
				_logger.Info("Cloud sync or auto-upload disabled, skipping upload");
				return;
			}
			if (climbData == null)
			{
				_logger.Error("Cannot queue null climb data for upload");
				return;
			}
			if (!InputValidator.IsValidLevelId(levelId))
			{
				_logger.Error("Cannot queue climb - invalid level ID: " + levelId);
				return;
			}
			if (!InputValidator.IsValidPointCount(climbData.Points?.Count ?? 0))
			{
				_logger.Error($"Cannot queue climb - invalid point count: {climbData.Points?.Count ?? 0}");
				return;
			}
			if (_uploadQueue.Any((UploadQueueItem x) => x.ClimbData.Id == climbData.Id))
			{
				_logger.Info($"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.Info($"Added climb {climbData.Id} to upload queue. Queue size: {QueuedUploads}");
			if (!_isProcessingQueue)
			{
				ProcessQueue();
			}
		}

		public void ProcessQueue()
		{
			if (_isProcessingQueue)
			{
				_logger.Info("Upload queue already being processed");
				return;
			}
			_isProcessingQueue = true;
			_logger.Info($"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.Info("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.Info("Upload queue processing completed");
				_isProcessingQueue = false;
				SaveQueue();
				return;
			}
			UploadQueueItem item = items[index];
			if (item == null || item.ClimbData == null)
			{
				_logger.Warning($"Skipping invalid upload queue item at index {index}");
				ProcessNextItem(items, index + 1);
				return;
			}
			if (item.ClimbData.WasFlagged)
			{
				_logger.Warning($"[FlyDetection] Processing flagged climb {item.ClimbData.Id}");
				_logger.Warning($"[FlyDetection] Score: {item.ClimbData.FlaggedScore}/100");
				_logger.Warning("[FlyDetection] Will upload as private to server");
			}
			if (!_configService.Config.CanUpload())
			{
				_logger.Warning("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.Info($"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,
				AscentLevel = item.ClimbData.AscentLevel
			};
			bool wasFlagged = item.ClimbData.WasFlagged;
			float flaggedScore = item.ClimbData.FlaggedScore;
			string flaggedReason = item.ClimbData.FlaggedReason;
			_apiService.UploadClimbWithDetection(climbData, levelId, wasFlagged, flaggedScore, flaggedReason, delegate(bool success, string error)
			{
				if (success)
				{
					item.Status = UploadStatus.Completed;
					_logger.Info($"Successfully uploaded climb {item.ClimbData.Id}");
				}
				else
				{
					item.RetryCount++;
					item.LastError = error;
					if (item.ShouldRetry(_configService.Config.RetryAttempts))
					{
						item.Status = UploadStatus.Failed;
						_logger.Warning($"Upload failed for climb {item.ClimbData.Id}: {error}. Will retry ({item.RetryCount}/{_configService.Config.RetryAttempts})");
					}
					else
					{
						item.Status = UploadStatus.Failed;
						_logger.Error($"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.Info("No failed uploads to retry");
				return;
			}
			_logger.Info($"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.Info($"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.Info($"Removed {list.Count} expired items from upload queue");
			}
		}

		private void SaveQueue()
		{
			List<UploadQueueItem> payload = new List<UploadQueueItem>(_uploadQueue);
			FileUtils.WriteJsonFileInBackground(_logger, _queueFilePath, payload);
		}

		private void LoadQueue()
		{
			try
			{
				if (!File.Exists(_queueFilePath))
				{
					return;
				}
				string text = File.ReadAllText(_queueFilePath);
				_uploadQueue = JsonConvert.DeserializeObject<List<UploadQueueItem>>(text, CommonJsonSettings.Default) ?? 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.Info($"Loaded upload queue with {_uploadQueue.Count} items");
			}
			catch (Exception ex)
			{
				_logger.Error("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 ModLogger _logger;

		private readonly string _configPath;

		private ServerConfig _config;

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

		public ServerConfigService(ModLogger 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, CommonJsonSettings.Default);
					_logger.Info("Server configuration loaded successfully");
				}
				else
				{
					_config = CreateDefaultConfig();
					SaveConfig();
					_logger.Info("Created default server configuration");
				}
			}
			catch (Exception ex)
			{
				_logger.Error("Failed to load server config: " + ex.Message);
				_config = CreateDefaultConfig();
			}
			return _config;
		}

		public void SaveConfig()
		{
			FileUtils.WriteJsonFileInBackground(_logger, _configPath, _config);
		}

		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.Info("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.Info("Player name updated to: " + _config.PlayerName);
		}

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

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

			private object <>2__current;

			public VPSApiService <>4__this;

			public string modVersion;

			public Action<UpdateMessage> 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 <CheckForUpdateMessageCoroutine>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_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/updates/check/" + UnityWebRequest.EscapeURL(modVersion);
						<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;
						if ((int)<request>5__2.result == 1)
						{
							try
							{
								UpdateMessageResponse updateMessageResponse = JsonConvert.DeserializeObject<UpdateMessageResponse>(<request>5__2.downloadHandler.text, CommonJsonSettings.Default);
								UpdateMessage updateMessage = new UpdateMessage
								{
									HasUpdate = updateMessageResponse.HasUpdate,
									Message = updateMessageResponse.Message,
									Type = (updateMessageResponse.Type ?? "info"),
									LastChecked = DateTime.Now
								};
								lock (vPSApiService._updateCacheLock)
								{
									vPSApiService._cachedUpdateMessage = updateMessage;
								}
								vPSApiService._logger.Info($"[UpdateMessage] Check complete - HasUpdate: {updateMessage.HasUpdate}");
								callback?.Invoke(updateMessage);
							}
							catch (Exception ex)
							{
								vPSApiService._logger.Error("[UpdateMessage] Failed to parse response: " + ex.Message);
								callback?.Invoke(new UpdateMessage
								{
									HasUpdate = false
								});
							}
						}
						else
						{
							vPSApiService._logger.Warning("[UpdateMessage] Check failed: " + <request>5__2.error);
							callback?.Invoke(new UpdateMessage
							{
								HasUpdate = false
							});
						}
						<>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 <CheckServerHealthCoroutine>d__15 : 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__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_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, CommonJsonSettings.Default);
								vPSApiService.IsServerReachable = healthResponse.Status == "healthy";
								if (vPSApiService.IsServerReachable)
								{
									vPSApiService._logger.Info($"Server health check successful. {healthResponse.Stats.TotalClimbs} climbs in database.");
								}
								else
								{
									vPSApiService._logger.Warning("Server unhealthy: " + healthResponse.Status);
									vPSApiService.IsServerReachable = false;
								}
							}
							catch (Exception ex)
							{
								vPSApiService._logger.Error("Failed to parse health response: " + ex.Message);
								vPSApiService.IsServerReachable = false;
							}
						}
						else
						{
							vPSApiService._logger.Error("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__23 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public string levelId;

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

			public VPSApiService <>4__this;

			public int limit;

			public int offset;

			public string playerName;

			public string biomeName;

			public string peakCode;

			public int? ascentLevel;

			public string sortBy;

			public string sortOrder;

			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__23(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_01f1: Unknown result type (might be due to invalid IL or missing references)
				//IL_01f7: 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", null);
							return false;
						}
						StringBuilder stringBuilder = new StringBuilder($"{vPSApiService._config.BaseUrl}/api/climbs/{levelId}?limit={limit}&offset={offset}");
						if (!string.IsNullOrEmpty(playerName))
						{
							stringBuilder.Append("&player_name=" + UnityWebRequest.EscapeURL(playerName));
						}
						if (!string.IsNullOrEmpty(biomeName))
						{
							stringBuilder.Append("&biome_name=" + UnityWebRequest.EscapeURL(biomeName));
						}
						if (!string.IsNullOrEmpty(peakCode))
						{
							stringBuilder.Append("&peak_code=" + UnityWebRequest.EscapeURL(peakCode));
						}
						if (ascentLevel.HasValue)
						{
							stringBuilder.Append($"&ascent_level={ascentLevel.Value}");
						}
						stringBuilder.Append("&sort_by=" + sortBy + "&sort_order=" + sortOrder);
						stringBuilder.Append("&format=compressed");
						string text3 = stringBuilder.ToString();
						<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, CommonJsonSettings.Default);
								List<ClimbData> list = new List<ClimbData>();
								if (climbListResponse.Data != null)
								{
									foreach (ServerClimbData datum in climbListResponse.Data)
									{
										ClimbData climbData = datum.ToClimbData();
										if (!string.IsNullOrEmpty(datum.PointData))
										{
											vPSApiService._logger.Info($"Processing compressed climb {datum.Id}: PointData length={datum.PointData.Length}, Points count={climbData.Points?.Count ?? 0}");
										}
										list.Add(climbData);
									}
								}
								vPSApiService._logger.Info($"Downloaded {list.Count} climbs for level {levelId}");
								callback?.Invoke(list, null, climbListResponse.Meta);
							}
							catch (Exception ex)
							{
								string text = "Failed to parse download response: " + ex.Message;
								vPSApiService._logger.Error(text);
								callback?.Invoke(new List<ClimbData>(), text, null);
							}
						}
						else if (<request>5__2.responseCode == 404)
						{
							vPSApiService._logger.Info("No climbs found for level " + levelId);
							callback?.Invoke(new List<ClimbData>(), null, null);
						}
						else
						{
							string text2 = $"Download request failed: {<request>5__2.error} (HTTP {<request>5__2.responseCode})";
							vPSApiService._logger.Error(text2);
							callback?.Invoke(new List<ClimbData>(), text2, null);
						}
						<>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__27 : 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__27(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.Error(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__25 : 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__25(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 text3 = vPSApiService._config.BaseUrl + "/api/climbs/search/" + peakCode + "?format=compressed";
						<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, CommonJsonSettings.Default);
								if (climbSearchResponse.Success && climbSearchResponse.Data != null)
								{
									ClimbData climbData = climbSearchResponse.Data.ToClimbData();
									vPSApiService._logger.Info("Found climb with peak code " + peakCode + ": " + climbData.GetDisplayName());
									callback?.Invoke(climbData, null);
								}
								else
								{
									vPSApiService._logger.Info("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.Error(text);
								callback?.Invoke(null, text);
							}
						}
						else if (<request>5__2.responseCode == 404)
						{
							vPSApiService._logger.Info("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.Error(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__20 : 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__20(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_0321: Unknown result type (might be due to invalid IL or missing references)
				//IL_0327: Invalid comparison between Unknown and I4
				//IL_027a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0284: Expected O, but got Unknown
				//IL_0294: Unknown result type (might be due to invalid IL or missing references)
				//IL_029e: Expected O, but got Unknown
				//IL_02a4: Unknown result type (might be due to invalid IL or missing references)
				//IL_02ae: 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";
						int num2 = InputValidator.ClampAscentLevel(climbData.AscentLevel);
						vPSApiService._logger.Info($"Upload data: AscentLevel from ClimbData: {climbData.AscentLevel}, Clamped: {num2}");
						MemoryStream memoryStream = new MemoryStream();
						ClimbDataCrusher.WriteClimbData(memoryStream, climbData);
						byte[] array = memoryStream.ToArray();
						string text3 = JsonConvert.SerializeObject((object)new
						{
							levelId = levelId,
							playerName = InputValidator.SanitizePlayerName(vPSApiService._config.PlayerName),
							biomeName = InputValidator.SanitizeBiomeName(climbData.BiomeName),
							duration = InputValidator.ClampDuration(climbData.DurationInSeconds),
							pointData = Convert.ToBase64String(array),
							compressionVersion = 1,
							isSuccessful = true,
							tags = new string[0],
							ascentLevel = num2
						}, CommonJsonSettings.Compact);
						vPSApiService._logger.Info($"Using compressed upload format. Original points: {climbData.Points.Count}, Compressed size: {array.Length} bytes");
						int num3 = text3.IndexOf("\"ascentLevel\":");
						if (num3 >= 0)
						{
							int num4 = Math.Min(num3 + 50, text3.Length);
							string text4 = text3.Substring(num3, num4 - num3);
							vPSApiService._logger.Info("Upload JSON contains ascentLevel: " + text4);
						}
						else
						{
							vPSApiService._logger.Error("Upload JSON does NOT contain ascentLevel field!");
						}
						vPSApiService._logger.Info("Upload JSON payload (first 500 chars): " + text3.Substring(0, Math.Min(500, text3.Length)) + "...");
						if (text3.Length > vPSApiService.GetMaxPayloadSize())
						{
							string text5 = $"Payload too large ({text3.Length} bytes). Consider reducing climb complexity.";
							vPSApiService._logger.Error(text5);
							callback?.Invoke(arg1: false, text5);
							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, CommonJsonSettings.Default);
								if (apiResponse.Success)
								{
									vPSApiService._config.IncrementUploadCount();
									vPSApiService._logger.Info("Climb uploaded successfully: " + apiResponse.Data.ClimbId);
									callback?.Invoke(arg1: true, apiResponse.Data.ClimbId);
								}
								else
								{
									vPSApiService._logger.Error("Upload failed: " + apiResponse.Error);
									callback?.Invoke(arg1: false, apiResponse.Error);
								}
							}
							catch (Exception ex)
							{
								vPSApiService._logger.Error("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.Error(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();
			}
		}

		[CompilerGenerated]
		private sealed class <UploadClimbWithDetectionCoroutine>d__21 : 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;

			public bool isFlagged;

			public float detectionScore;

			public string detectionReason;

			private UnityWebRequest <request>5__2;

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

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

			[DebuggerHidden]
			public <UploadClimbWithDetectionCoroutine>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_0298: Unknown result type (might be due to invalid IL or missing references)
				//IL_029e: Invalid comparison between Unknown and I4
				//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
				//IL_01fb: Expected O, but got Unknown
				//IL_020b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0215: Expected O, but got Unknown
				//IL_021b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0225: 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";
						int num2 = InputValidator.ClampAscentLevel(climbData.AscentLevel);
						vPSApiService._logger.Info($"Upload data: AscentLevel from ClimbData: {climbData.AscentLevel}, Clamped: {num2}");
						MemoryStream memoryStream = new MemoryStream();
						ClimbDataCrusher.WriteClimbData(memoryStream, climbData);
						byte[] inArray = memoryStream.ToArray();
						string text3 = JsonConvert.SerializeObject((object)new
						{
							levelId = levelId,
							playerName = InputValidator.SanitizePlayerName(vPSApiService._config.PlayerName),
							biomeName = InputValidator.SanitizeBiomeName(climbData.BiomeName),
							duration = InputValidator.ClampDuration(climbData.DurationInSeconds),
							pointData = Convert.ToBase64String(inArray),
							compressionVersion = 1,
							isSuccessful = true,
							tags = new string[0],
							ascentLevel = num2,