Decompiled source of PortalOresAndTeleportMarkers v2.3.1

plugins\PortalOresAndTeleportMarkers\UnrestrictedMineralPortals.dll

Decompiled 20 hours ago
using System;
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.Serialization;
using System.Runtime.Serialization.Json;
using System.Runtime.Versioning;
using System.Text;
using AndersonDavid.UnrestrictedMineralPortals.Commands;
using AndersonDavid.UnrestrictedMineralPortals.Configuration;
using AndersonDavid.UnrestrictedMineralPortals.Localization;
using AndersonDavid.UnrestrictedMineralPortals.Models;
using AndersonDavid.UnrestrictedMineralPortals.Patches;
using AndersonDavid.UnrestrictedMineralPortals.Persistence;
using AndersonDavid.UnrestrictedMineralPortals.Services;
using AndersonDavid.UnrestrictedMineralPortals.Utilities;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("masukito")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © masukito 2026")]
[assembly: AssemblyDescription("Transport ores and restricted items through Valheim portals, create personal teleport markers and travel to them using /tp commands.")]
[assembly: AssemblyFileVersion("2.3.1.0")]
[assembly: AssemblyInformationalVersion("2.3.1+178cf3f3ad62dcff9811deaa21d1cad47e627423")]
[assembly: AssemblyProduct("Portal Ores & Teleport Markers")]
[assembly: AssemblyTitle("Portal Ores & Teleport Markers")]
[assembly: AssemblyVersion("2.3.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[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 AndersonDavid.UnrestrictedMineralPortals
{
	internal static class ModInfo
	{
		public const string ProjectName = "UnrestrictedMineralPortals";

		public const string DisplayName = "Portal Ores & Teleport Markers";

		public const string AssemblyName = "UnrestrictedMineralPortals";

		public const string Guid = "com.andersondavid.valheim.unrestrictedmineralportals";

		public const string Version = "2.3.1";

		public const string Author = "masukito";

		public const string Authors = "Anderson David & masukito";
	}
	[BepInPlugin("com.andersondavid.valheim.unrestrictedmineralportals", "Portal Ores & Teleport Markers", "2.3.1")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private Harmony? _harmony;

		private ConsoleCommand? _teleportMarkerCommand;

		private TeleportMarkerService? _teleportMarkerService;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			try
			{
				ModConfig.Bind(((BaseUnityPlugin)this).Config);
				TeleportMarkerConfig.Bind(((BaseUnityPlugin)this).Config);
				LanguageManager.Initialize();
				_harmony = new Harmony("com.andersondavid.valheim.unrestrictedmineralportals");
				InitializeTeleportMarkers();
				MethodInfo methodInfo = GameCompatibility.FindTargetMethod();
				if ((object)methodInfo == null)
				{
					Log.LogError((object)"Compatibility check failed: Inventory.IsTeleportable() was not found. No patches were applied; original game behavior is preserved.");
					return;
				}
				MethodInfo methodInfo2 = AccessTools.DeclaredMethod(typeof(PortalRestrictionPatch), "Postfix", (Type[])null, (Type[])null);
				_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Log.LogInfo((object)"Patch strategy: postfix on Inventory.IsTeleportable().");
				ApplyItemIconPatches();
				Log.LogInfo((object)"Portal Ores & Teleport Markers 2.3.1 loaded successfully.");
			}
			catch (Exception arg)
			{
				Harmony? harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
				_harmony = null;
				Log.LogError((object)string.Format("Failed to initialize {0}; original game behavior is preserved.\n{1}", "Portal Ores & Teleport Markers", arg));
			}
		}

		private void InitializeTeleportMarkers()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Invalid comparison between Unknown and I4
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Expected O, but got Unknown
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Expected O, but got Unknown
			try
			{
				string path = Path.Combine(Paths.ConfigPath, "UnrestrictedMineralPortals", "teleport-markers.json");
				if ((int)SystemInfo.graphicsDeviceType == 4)
				{
					if (File.Exists(path))
					{
						Log.LogWarning((object)"A server-synchronized marker file was found but server synchronization is disabled in this version. The file was preserved.");
					}
					Log.LogInfo((object)"Personal teleport markers are not initialized on a dedicated server.");
					return;
				}
				TeleportMarkerRepository teleportMarkerRepository = new TeleportMarkerRepository(path);
				int num = teleportMarkerRepository.Load();
				if (teleportMarkerRepository.LoadError != null)
				{
					Log.LogError((object)("Failed to read teleport marker data. Existing data will not be overwritten: " + teleportMarkerRepository.LoadError));
				}
				_teleportMarkerService = new TeleportMarkerService(teleportMarkerRepository);
				TeleportService teleport = new TeleportService();
				ConnectedPlayerService connectedPlayerService = new ConnectedPlayerService();
				TeleportCommandHandler teleportCommandHandler = new TeleportCommandHandler(_teleportMarkerService, teleport, connectedPlayerService);
				_teleportMarkerCommand = new ConsoleCommand("tp", "Personal teleport markers", new ConsoleEvent(teleportCommandHandler.Handle), false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				ApplyAutocompletePatches(new TeleportAutoCompleteController(_teleportMarkerService, connectedPlayerService));
				Log.LogInfo((object)"Player teleport command initialized successfully.");
				Log.LogInfo((object)$"Local teleport markers initialized successfully. {num} marker(s) loaded.");
				if (ModConfig.DebugLogging.Value)
				{
					Log.LogDebug((object)("Teleport marker storage: " + teleportMarkerRepository.FilePath));
				}
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Failed to initialize teleport markers. Portal features remain available.\n{arg}");
			}
		}

		private void Update()
		{
			LanguageManager.Tick();
			ItemRestrictionUiService.Tick();
		}

		private void ApplyAutocompletePatches(TeleportAutoCompleteController controller)
		{
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Expected O, but got Unknown
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Expected O, but got Unknown
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Expected O, but got Unknown
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Expected O, but got Unknown
			MethodInfo methodInfo = null;
			MethodInfo methodInfo2 = null;
			MethodInfo methodInfo3 = null;
			MethodInfo methodInfo4 = null;
			try
			{
				methodInfo = AccessTools.DeclaredMethod(typeof(Terminal), "UpdateInput", Type.EmptyTypes, (Type[])null);
				methodInfo2 = AccessTools.DeclaredMethod(typeof(Chat), "Hide", Type.EmptyTypes, (Type[])null);
				methodInfo3 = AccessTools.DeclaredMethod(typeof(Chat), "SendInput", Type.EmptyTypes, (Type[])null);
				methodInfo4 = AccessTools.DeclaredMethod(typeof(Chat), "Update", Type.EmptyTypes, (Type[])null);
				if ((object)methodInfo == null || (object)methodInfo2 == null || (object)methodInfo3 == null || (object)methodInfo4 == null || !ChatAutoCompletePatch.IsCompatible)
				{
					throw new MissingMethodException("Expected Terminal.UpdateInput(), Chat.Update(), Chat.Hide(), Chat.SendInput(), or Terminal.m_input was not found.");
				}
				ChatAutoCompletePatch.Initialize(controller);
				MethodInfo methodInfo5 = AccessTools.DeclaredMethod(typeof(ChatAutoCompletePatch), "UpdateInputPrefix", (Type[])null, (Type[])null);
				MethodInfo methodInfo6 = AccessTools.DeclaredMethod(typeof(ChatAutoCompletePatch), "CancelPostfix", (Type[])null, (Type[])null);
				MethodInfo methodInfo7 = AccessTools.DeclaredMethod(typeof(ChatAutoCompletePatch), "ChatUpdatePostfix", (Type[])null, (Type[])null);
				_harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo5), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				_harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(methodInfo6), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				_harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(methodInfo6), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				_harmony.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, new HarmonyMethod(methodInfo7), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Log.LogInfo((object)"Teleport marker autocomplete initialized successfully.");
				if (ModConfig.DebugLogging.Value)
				{
					Log.LogDebug((object)"Autocomplete target: Terminal.UpdateInput() on focused Chat instances; input: Terminal.m_input (TMP_InputField).");
				}
			}
			catch (Exception arg)
			{
				if ((object)methodInfo != null)
				{
					Harmony? harmony = _harmony;
					if (harmony != null)
					{
						harmony.Unpatch((MethodBase)methodInfo, (HarmonyPatchType)1, "com.andersondavid.valheim.unrestrictedmineralportals");
					}
				}
				if ((object)methodInfo2 != null)
				{
					Harmony? harmony2 = _harmony;
					if (harmony2 != null)
					{
						harmony2.Unpatch((MethodBase)methodInfo2, (HarmonyPatchType)2, "com.andersondavid.valheim.unrestrictedmineralportals");
					}
				}
				if ((object)methodInfo3 != null)
				{
					Harmony? harmony3 = _harmony;
					if (harmony3 != null)
					{
						harmony3.Unpatch((MethodBase)methodInfo3, (HarmonyPatchType)2, "com.andersondavid.valheim.unrestrictedmineralportals");
					}
				}
				if ((object)methodInfo4 != null)
				{
					Harmony? harmony4 = _harmony;
					if (harmony4 != null)
					{
						harmony4.Unpatch((MethodBase)methodInfo4, (HarmonyPatchType)2, "com.andersondavid.valheim.unrestrictedmineralportals");
					}
				}
				Log.LogError((object)$"Failed to initialize teleport marker autocomplete. Other mod features remain available.\n{arg}");
			}
		}

		private void ApplyItemIconPatches()
		{
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Expected O, but got Unknown
			MethodInfo methodInfo = null;
			try
			{
				Log.LogInfo((object)(ItemRestrictionUiService.IsCompatible ? "RestrictionIconPatch: Active (timed InventoryGrid element refresh)." : "RestrictionIconPatch: Failed (compatible element fields not found). Portal transport remains active."));
			}
			catch (Exception ex)
			{
				Log.LogError((object)("RestrictionIconPatch: Failed. Portal transport remains active.\n" + ex));
			}
			try
			{
				methodInfo = GameCompatibility.FindTooltipMethod();
				MethodInfo methodInfo2 = AccessTools.DeclaredMethod(typeof(ItemTeleportRestrictionIconPatch), "TooltipPostfix", (Type[])null, (Type[])null);
				if ((object)methodInfo == null || (object)methodInfo2 == null)
				{
					Log.LogWarning((object)"TooltipPatch: Failed (compatible method not found). Icon hiding remains active.");
				}
				else
				{
					_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.LogInfo((object)"TooltipPatch: Active.");
				}
			}
			catch (Exception arg)
			{
				if ((object)methodInfo != null)
				{
					_harmony.Unpatch((MethodBase)methodInfo, (HarmonyPatchType)2, "com.andersondavid.valheim.unrestrictedmineralportals");
				}
				Log.LogError((object)$"TooltipPatch: Failed. Icon hiding and portal transport remain active.\n{arg}");
			}
			if (ModConfig.DebugLogging.Value)
			{
				Log.LogDebug((object)GameCompatibility.CompatibilityReport());
			}
		}

		private void OnDestroy()
		{
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			_harmony = null;
		}
	}
}
namespace AndersonDavid.UnrestrictedMineralPortals.Utilities
{
	internal static class GameCompatibility
	{
		internal const string TargetType = "Inventory";

		internal const string TargetMethod = "IsTeleportable";

		internal static MethodInfo? FindTargetMethod()
		{
			Type type = AccessTools.TypeByName("Inventory");
			if ((object)type != null)
			{
				return AccessTools.DeclaredMethod(type, "IsTeleportable", Type.EmptyTypes, (Type[])null);
			}
			return null;
		}

		internal static FieldInfo? FindInventoryElementsField()
		{
			return AccessTools.DeclaredField(typeof(InventoryGrid), "m_elements");
		}

		internal static FieldInfo? FindRestrictionIconField()
		{
			Type type = AccessTools.Inner(typeof(InventoryGrid), "Element");
			if ((object)type != null)
			{
				return AccessTools.DeclaredField(type, "m_noteleport");
			}
			return null;
		}

		internal static MethodInfo? FindTooltipMethod()
		{
			Type type = AccessTools.Inner(typeof(ItemDrop), "ItemData");
			if ((object)type != null)
			{
				return AccessTools.DeclaredMethod(type, "GetTooltip", new Type[5]
				{
					type,
					typeof(int),
					typeof(bool),
					typeof(float),
					typeof(int)
				}, (Type[])null);
			}
			return null;
		}

		internal static string CompatibilityReport()
		{
			return "Valheim compatibility report:\n- Inventory grid update method: none exists; timed active-grid refresh used\n- Restriction icon member: " + (FindRestrictionIconField()?.ToString() ?? "not found") + "\n- Tooltip method: " + (FindTooltipMethod()?.ToString() ?? "not found");
		}
	}
}
namespace AndersonDavid.UnrestrictedMineralPortals.Services
{
	internal sealed class ConnectedPlayerService
	{
		internal enum CurrentState
		{
			Ready,
			Disconnected,
			PositionUnavailable,
			PositionNotShared
		}

		internal enum PositionSource
		{
			PlayerTransform,
			Zdo,
			PublicMapData
		}

		internal bool IsAvailable => ZNet.instance != null;

		internal List<ConnectedPlayerInfo> ListOthers()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			if (!IsAvailable)
			{
				return new List<ConnectedPlayerInfo>();
			}
			ZDOID local = ZNet.instance.LocalPlayerCharacterID;
			List<ConnectedPlayerInfo> list = (from x in ZNet.instance.GetPlayerList()
				where x.m_characterID != local && !string.IsNullOrWhiteSpace(x.m_name)
				select new ConnectedPlayerInfo(x.m_characterID, x.m_name.Trim(), PlatformDisplayName(x)) into x
				group x by x.CharacterId into x
				select x.First()).OrderBy<ConnectedPlayerInfo, string>((ConnectedPlayerInfo x) => x.Name, StringComparer.CurrentCultureIgnoreCase).ToList();
			if (ModConfig.DebugLogging.Value)
			{
				foreach (ConnectedPlayerInfo item in list)
				{
					Player val = FindActivePlayer(item.CharacterId);
					ManualLogSource log = Plugin.Log;
					object[] obj = new object[5]
					{
						item.CharacterName,
						item.PlatformDisplayName,
						val != null,
						val != null,
						null
					};
					ZDOMan instance = ZDOMan.instance;
					obj[4] = ((instance != null) ? instance.GetZDO(item.CharacterId) : null) != null;
					log.LogDebug((object)string.Format("Connected player discovered: characterName='{0}', platformDisplayName='{1}', hasPlayerInstance={2}, hasTransform={3}, hasZNetView={4}.", obj));
				}
			}
			return list;
		}

		internal CurrentState ResolveCurrent(ConnectedPlayerInfo player, out Vector3 position, out Quaternion rotation, out PositionSource source)
		{
			//IL_000e: 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_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			position = default(Vector3);
			rotation = Quaternion.identity;
			source = PositionSource.PlayerTransform;
			if (!IsAvailable)
			{
				return CurrentState.Disconnected;
			}
			PlayerInfo val = ((IEnumerable<PlayerInfo>)ZNet.instance.GetPlayerList()).FirstOrDefault((Func<PlayerInfo, bool>)((PlayerInfo x) => x.m_characterID == player.CharacterId));
			if (val.m_characterID != player.CharacterId)
			{
				return CurrentState.Disconnected;
			}
			Player val2 = FindActivePlayer(player.CharacterId);
			if (val2 != null)
			{
				position = ((Component)val2).transform.position;
				rotation = ((Component)val2).transform.rotation;
				source = PositionSource.PlayerTransform;
				if (Valid(position) && Valid(rotation))
				{
					return CurrentState.Ready;
				}
			}
			ZDOMan instance = ZDOMan.instance;
			ZDO val3 = ((instance != null) ? instance.GetZDO(player.CharacterId) : null);
			if (val3 != null)
			{
				position = val3.GetPosition();
				rotation = val3.GetRotation();
				source = PositionSource.Zdo;
				if (Valid(position) && Valid(rotation))
				{
					return CurrentState.Ready;
				}
			}
			if (!val.m_publicPosition)
			{
				return CurrentState.PositionNotShared;
			}
			position = val.m_position;
			rotation = Quaternion.identity;
			source = PositionSource.PublicMapData;
			if (!Valid(position))
			{
				return CurrentState.PositionUnavailable;
			}
			return CurrentState.Ready;
		}

		private static Player? FindActivePlayer(ZDOID characterId)
		{
			//IL_0007: 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)
			return ((IEnumerable<Player>)Player.GetAllPlayers()).FirstOrDefault((Func<Player, bool>)((Player player) => player != null && (Object)(object)player != (Object)(object)Player.m_localPlayer && ((Component)player).gameObject.activeInHierarchy && ((Character)player).GetZDOID() == characterId));
		}

		private static string PlatformDisplayName(PlayerInfo player)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return player.m_serverAssignedDisplayName ?? string.Empty;
		}

		private static bool Valid(Vector3 value)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (Finite(value.x) && Finite(value.y))
			{
				return Finite(value.z);
			}
			return false;
		}

		private static bool Valid(Quaternion value)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			if (Finite(value.x) && Finite(value.y) && Finite(value.z))
			{
				return Finite(value.w);
			}
			return false;
		}

		private static bool Finite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}
	}
	internal static class ItemRestrictionUiService
	{
		internal const string TooltipLocalizationKey = "$item_noteleport";

		private const string TooltipMarkup = "<color=orange>$item_noteleport</color>";

		private static readonly FieldInfo? ElementsField = GameCompatibility.FindInventoryElementsField();

		private static readonly FieldInfo? NoTeleportField = GameCompatibility.FindRestrictionIconField();

		private static float _nextRefresh;

		private static bool _loggedComponentFound;

		private static bool _loggedComponentMissing;

		internal static bool IsCompatible
		{
			get
			{
				if ((object)ElementsField != null)
				{
					return (object)NoTeleportField != null;
				}
				return false;
			}
		}

		internal static void Tick()
		{
			if (ModConfig.Enabled.Value && IsCompatible && !(Time.unscaledTime < _nextRefresh))
			{
				_nextRefresh = Time.unscaledTime + 0.25f;
				InventoryGrid[] array = Object.FindObjectsByType<InventoryGrid>((FindObjectsSortMode)0);
				for (int i = 0; i < array.Length; i++)
				{
					HideRestrictionIcons(array[i]);
				}
			}
		}

		internal static int HideRestrictionIcons(InventoryGrid grid)
		{
			if (!ModConfig.Enabled.Value || grid == null || !(ElementsField?.GetValue(grid) is IEnumerable enumerable))
			{
				return 0;
			}
			int num = 0;
			foreach (object item in enumerable)
			{
				if (item != null)
				{
					object? obj = NoTeleportField?.GetValue(item);
					Component val = (Component)((obj is Component) ? obj : null);
					if (val != null && val.gameObject.activeSelf)
					{
						val.gameObject.SetActive(false);
						num++;
					}
				}
			}
			if (ModConfig.DebugLogging.Value && num > 0 && !_loggedComponentFound)
			{
				Plugin.Log.LogDebug((object)$"InventoryGrid.Element.m_noteleport found; hidden in {num} visible slot(s). Further UI updates will not be logged.");
				_loggedComponentFound = true;
			}
			else if (ModConfig.DebugLogging.Value && num == 0 && !_loggedComponentMissing)
			{
				Plugin.Log.LogDebug((object)"Active InventoryGrid scan completed without an active m_noteleport component to hide.");
				_loggedComponentMissing = true;
			}
			return num;
		}

		internal static string RemoveRestrictionTooltip(string tooltip)
		{
			if (!ModConfig.Enabled.Value || string.IsNullOrEmpty(tooltip))
			{
				return tooltip;
			}
			return tooltip.Replace("\n<color=orange>$item_noteleport</color>", string.Empty).Replace("<color=orange>$item_noteleport</color>", string.Empty);
		}
	}
	internal enum MarkerOperationStatus
	{
		Success,
		Duplicate,
		LimitReached,
		NotFound,
		SaveFailed
	}
	internal sealed class MarkerOperationResult
	{
		internal MarkerOperationStatus Status { get; set; }

		internal string? Error { get; set; }
	}
	internal static class PlayerIdentityService
	{
		internal static bool TryGet(out string playerId)
		{
			playerId = string.Empty;
			Game instance = Game.instance;
			PlayerProfile val = ((instance != null) ? instance.GetPlayerProfile() : null);
			if (val == null)
			{
				return false;
			}
			playerId = val.GetPlayerID().ToString(CultureInfo.InvariantCulture);
			return true;
		}
	}
	internal enum PlayerTeleportStatus
	{
		Success,
		Disconnected,
		PositionUnavailable,
		PositionNotShared,
		DestinationBlocked,
		PlayerUnavailable,
		InProgress,
		Cooldown,
		Failed
	}
	internal sealed class PlayerTeleportResult
	{
		internal PlayerTeleportStatus Status { get; set; }

		internal int RemainingSeconds { get; set; }
	}
	internal sealed class PlayerTeleportService
	{
		private readonly ConnectedPlayerService _players;

		private readonly TeleportService _teleport;

		internal PlayerTeleportService(ConnectedPlayerService players, TeleportService teleport)
		{
			_players = players;
			_teleport = teleport;
		}

		internal PlayerTeleportResult Teleport(Player local, string playerId, ConnectedPlayerInfo target, int cooldown)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position;
			Quaternion rotation;
			ConnectedPlayerService.PositionSource source;
			ConnectedPlayerService.CurrentState currentState = _players.ResolveCurrent(target, out position, out rotation, out source);
			if (ModConfig.DebugLogging.Value)
			{
				Plugin.Log.LogDebug((object)$"Resolving player teleport target: requestedName='{target.CharacterName}', matchedCharacterName='{target.CharacterName}', platformDisplayName='{target.PlatformDisplayName}', resolutionState={currentState}, positionSource={source}, currentPosition={position}.");
			}
			switch (currentState)
			{
			case ConnectedPlayerService.CurrentState.Disconnected:
				return Result(PlayerTeleportStatus.Disconnected);
			case ConnectedPlayerService.CurrentState.PositionUnavailable:
				return Result(PlayerTeleportStatus.PositionUnavailable);
			case ConnectedPlayerService.CurrentState.PositionNotShared:
				return Result(PlayerTeleportStatus.PositionNotShared);
			default:
			{
				if (!TrySafeDestination(local, position, rotation, out var destination))
				{
					return Result(PlayerTeleportStatus.DestinationBlocked);
				}
				TeleportAttemptResult teleportAttemptResult = _teleport.TeleportTo(local, playerId, destination, rotation, cooldown);
				if (ModConfig.DebugLogging.Value)
				{
					Plugin.Log.LogDebug((object)$"Player teleport destination resolved: characterName='{target.CharacterName}', distance={Vector3.Distance(((Component)local).transform.position, position):F1}, destination={destination}.");
				}
				return new PlayerTeleportResult
				{
					Status = Map(teleportAttemptResult.Status),
					RemainingSeconds = teleportAttemptResult.RemainingSeconds
				};
			}
			}
		}

		private static bool TrySafeDestination(Player local, Vector3 target, Quaternion rotation, out Vector3 destination)
		{
			//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_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = rotation * Vector3.right;
			val.y = 0f;
			if (((Vector3)(ref val)).sqrMagnitude < 0.01f)
			{
				val = Vector3.right;
			}
			else
			{
				((Vector3)(ref val)).Normalize();
			}
			Vector3 val2 = rotation * Vector3.forward;
			val2.y = 0f;
			if (((Vector3)(ref val2)).sqrMagnitude < 0.01f)
			{
				val2 = Vector3.forward;
			}
			else
			{
				((Vector3)(ref val2)).Normalize();
			}
			CapsuleCollider component = ((Component)local).GetComponent<CapsuleCollider>();
			float num = ((component == null) ? 0.45f : Mathf.Max(0.3f, component.radius * Mathf.Max(((Component)local).transform.lossyScale.x, ((Component)local).transform.lossyScale.z)));
			float height = ((component == null) ? 1.8f : Mathf.Max(num * 2f, component.height * ((Component)local).transform.lossyScale.y));
			float num2 = Mathf.Max(1.75f, num * 3.5f);
			Vector3[] array = (Vector3[])(object)new Vector3[4]
			{
				target + val * num2,
				target - val * num2,
				target - val2 * num2,
				target + val2 * num2
			};
			foreach (Vector3 val3 in array)
			{
				if (Free(val3, num, height, local))
				{
					destination = val3;
					return true;
				}
			}
			destination = default(Vector3);
			return false;
		}

		private static bool Free(Vector3 point, float radius, float height, Player local)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0025: 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)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = point + Vector3.up * (radius + 0.05f);
			Vector3 val2 = point + Vector3.up * (height - radius);
			return !Physics.OverlapCapsule(val, val2, radius, -1, (QueryTriggerInteraction)1).Any((Collider x) => x != null && !((Component)x).transform.IsChildOf(((Component)local).transform));
		}

		private static PlayerTeleportStatus Map(TeleportAttemptStatus status)
		{
			return status switch
			{
				TeleportAttemptStatus.Success => PlayerTeleportStatus.Success, 
				TeleportAttemptStatus.PlayerUnavailable => PlayerTeleportStatus.PlayerUnavailable, 
				TeleportAttemptStatus.InProgress => PlayerTeleportStatus.InProgress, 
				TeleportAttemptStatus.Cooldown => PlayerTeleportStatus.Cooldown, 
				_ => PlayerTeleportStatus.Failed, 
			};
		}

		private static PlayerTeleportResult Result(PlayerTeleportStatus status)
		{
			return new PlayerTeleportResult
			{
				Status = status
			};
		}
	}
	internal static class PortalRestrictionService
	{
		internal static bool ShouldAllowTeleport()
		{
			return ModConfig.Enabled?.Value ?? false;
		}
	}
	internal sealed class TeleportMarkerService
	{
		private readonly TeleportMarkerRepository _repository;

		internal int Revision { get; private set; }

		internal TeleportMarkerService(TeleportMarkerRepository repository)
		{
			_repository = repository;
		}

		internal MarkerOperationResult Create(TeleportMarker marker, int maximum)
		{
			MarkerOperationStatus status = MarkerOperationStatus.Success;
			string error;
			bool flag = _repository.MutateAndSave(delegate(TeleportMarkerDatabase database)
			{
				WorldTeleportMarkers world = GetWorld(database, marker.PlayerId, marker.WorldId, create: true);
				if (world.Markers.Any((TeleportMarker x) => NamesEqual(x.Name, marker.Name)))
				{
					status = MarkerOperationStatus.Duplicate;
					return false;
				}
				if (maximum > 0 && world.Markers.Count >= maximum)
				{
					status = MarkerOperationStatus.LimitReached;
					return false;
				}
				world.Markers.Add(marker);
				return true;
			}, out error);
			if (flag && status == MarkerOperationStatus.Success)
			{
				Revision++;
			}
			return new MarkerOperationResult
			{
				Status = (flag ? status : MarkerOperationStatus.SaveFailed),
				Error = error
			};
		}

		internal MarkerOperationResult Delete(string playerId, string worldId, string name)
		{
			MarkerOperationStatus status = MarkerOperationStatus.NotFound;
			string error;
			bool flag = _repository.MutateAndSave(delegate(TeleportMarkerDatabase database)
			{
				WorldTeleportMarkers world = GetWorld(database, playerId, worldId, create: false);
				TeleportMarker teleportMarker = world?.Markers.FirstOrDefault((TeleportMarker x) => NamesEqual(x.Name, name));
				if (teleportMarker == null)
				{
					return false;
				}
				world.Markers.Remove(teleportMarker);
				status = MarkerOperationStatus.Success;
				return true;
			}, out error);
			if (flag && status == MarkerOperationStatus.Success)
			{
				Revision++;
			}
			return new MarkerOperationResult
			{
				Status = (flag ? status : MarkerOperationStatus.SaveFailed),
				Error = error
			};
		}

		internal TeleportMarker? Find(string playerId, string worldId, string name)
		{
			return _repository.Read((TeleportMarkerDatabase database) => GetWorld(database, playerId, worldId, create: false)?.Markers.FirstOrDefault((TeleportMarker x) => NamesEqual(x.Name, name)));
		}

		internal TeleportMarker? FindInOtherWorld(string playerId, string worldId, string name)
		{
			PlayerTeleportMarkers value;
			return _repository.Read((TeleportMarkerDatabase database) => (!database.Players.TryGetValue(playerId, out value)) ? null : value.Worlds.Where<KeyValuePair<string, WorldTeleportMarkers>>((KeyValuePair<string, WorldTeleportMarkers> x) => x.Key != worldId).SelectMany((KeyValuePair<string, WorldTeleportMarkers> x) => x.Value.Markers).FirstOrDefault((TeleportMarker x) => NamesEqual(x.Name, name)));
		}

		internal List<TeleportMarker> List(string playerId, string worldId)
		{
			return _repository.Read((TeleportMarkerDatabase database) => (GetWorld(database, playerId, worldId, create: false)?.Markers ?? new List<TeleportMarker>()).OrderBy<TeleportMarker, string>((TeleportMarker x) => x.Name, StringComparer.CurrentCultureIgnoreCase).ToList());
		}

		internal int CountOtherWorlds(string playerId, string worldId)
		{
			PlayerTeleportMarkers value;
			return _repository.Read((TeleportMarkerDatabase database) => database.Players.TryGetValue(playerId, out value) ? value.Worlds.Where<KeyValuePair<string, WorldTeleportMarkers>>((KeyValuePair<string, WorldTeleportMarkers> x) => x.Key != worldId).Sum((KeyValuePair<string, WorldTeleportMarkers> x) => x.Value.Markers.Count) : 0);
		}

		internal static string NormalizeName(string name)
		{
			return name.Trim();
		}

		private static bool NamesEqual(string left, string right)
		{
			return string.Equals(TeleportMarkerMatcher.Normalize(left), TeleportMarkerMatcher.Normalize(right), StringComparison.Ordinal);
		}

		private static WorldTeleportMarkers? GetWorld(TeleportMarkerDatabase database, string playerId, string worldId, bool create)
		{
			if (!database.Players.TryGetValue(playerId, out PlayerTeleportMarkers value))
			{
				if (!create)
				{
					return null;
				}
				value = new PlayerTeleportMarkers();
				database.Players[playerId] = value;
			}
			if (!value.Worlds.TryGetValue(worldId, out WorldTeleportMarkers value2) && create)
			{
				value2 = new WorldTeleportMarkers();
				value.Worlds[worldId] = value2;
			}
			return value2;
		}
	}
	internal enum TeleportAttemptStatus
	{
		Success,
		PlayerUnavailable,
		InProgress,
		Cooldown,
		Failed
	}
	internal sealed class TeleportAttemptResult
	{
		internal TeleportAttemptStatus Status { get; set; }

		internal int RemainingSeconds { get; set; }
	}
	internal sealed class TeleportService
	{
		private readonly Dictionary<string, DateTime> _lastTeleports = new Dictionary<string, DateTime>(StringComparer.Ordinal);

		internal TeleportAttemptResult Teleport(Player player, string playerId, TeleportMarker marker, int cooldownSeconds)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			return TeleportTo(player, playerId, new Vector3(marker.PositionX, marker.PositionY, marker.PositionZ), new Quaternion(marker.RotationX, marker.RotationY, marker.RotationZ, marker.RotationW), cooldownSeconds);
		}

		internal TeleportAttemptResult TeleportTo(Player player, string playerId, Vector3 position, Quaternion rotation, int cooldownSeconds)
		{
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			if (player == null || Game.instance == null || ZNet.instance == null || ((Character)player).IsDead() || Game.instance.WaitingForRespawn())
			{
				return Result(TeleportAttemptStatus.PlayerUnavailable);
			}
			if (((Character)player).IsTeleporting())
			{
				return Result(TeleportAttemptStatus.InProgress);
			}
			DateTime utcNow = DateTime.UtcNow;
			if (cooldownSeconds > 0 && _lastTeleports.TryGetValue(playerId, out var value))
			{
				double num = (double)cooldownSeconds - (utcNow - value).TotalSeconds;
				if (num > 0.0)
				{
					return new TeleportAttemptResult
					{
						Status = TeleportAttemptStatus.Cooldown,
						RemainingSeconds = Math.Max(1, (int)Math.Ceiling(num))
					};
				}
			}
			if (!((Character)player).TeleportTo(position, rotation, true))
			{
				return Result(TeleportAttemptStatus.Failed);
			}
			_lastTeleports[playerId] = utcNow;
			return Result(TeleportAttemptStatus.Success);
		}

		private static TeleportAttemptResult Result(TeleportAttemptStatus status)
		{
			return new TeleportAttemptResult
			{
				Status = status
			};
		}
	}
	internal static class WorldIdentityService
	{
		internal static bool TryGet(out string worldId)
		{
			worldId = string.Empty;
			if (ZNet.instance == null)
			{
				return false;
			}
			worldId = ZNet.instance.GetWorldUID().ToString(CultureInfo.InvariantCulture);
			return true;
		}
	}
}
namespace AndersonDavid.UnrestrictedMineralPortals.Persistence
{
	internal sealed class TeleportMarkerRepository
	{
		private readonly object _sync = new object();

		private readonly string _path;

		private TeleportMarkerDatabase _database = new TeleportMarkerDatabase();

		private bool _writesAllowed = true;

		internal string FilePath => _path;

		internal string? LoadError { get; private set; }

		internal TeleportMarkerRepository(string path)
		{
			_path = path;
		}

		internal int Load()
		{
			lock (_sync)
			{
				if (!File.Exists(_path) || new FileInfo(_path).Length == 0L)
				{
					return 0;
				}
				try
				{
					using FileStream fileStream = File.OpenRead(_path);
					_database = ((TeleportMarkerDatabase)((XmlObjectSerializer)CreateSerializer()).ReadObject((Stream)fileStream)) ?? new TeleportMarkerDatabase();
					Normalize(_database);
					return CountMarkers(_database);
				}
				catch (Exception ex)
				{
					LoadError = ex.Message;
					_writesAllowed = false;
					return 0;
				}
			}
		}

		internal T Read<T>(Func<TeleportMarkerDatabase, T> operation)
		{
			lock (_sync)
			{
				return operation(_database);
			}
		}

		internal bool MutateAndSave(Func<TeleportMarkerDatabase, bool> operation, out string? error)
		{
			lock (_sync)
			{
				if (!_writesAllowed)
				{
					error = "O arquivo existente não pôde ser carregado; a gravação foi bloqueada para preservar os dados.";
					return false;
				}
				if (!operation(_database))
				{
					error = null;
					return true;
				}
				return SaveLocked(out error);
			}
		}

		private bool SaveLocked(out string? error)
		{
			string directoryName = Path.GetDirectoryName(_path);
			string text = _path + ".tmp";
			string destFileName = _path + ".bak";
			try
			{
				Directory.CreateDirectory(directoryName);
				using (FileStream fileStream = new FileStream(text, FileMode.Create, FileAccess.Write, FileShare.None))
				{
					((XmlObjectSerializer)CreateSerializer()).WriteObject((Stream)fileStream, (object)_database);
					fileStream.Flush(flushToDisk: true);
				}
				if (File.Exists(_path))
				{
					File.Copy(_path, destFileName, overwrite: true);
					try
					{
						File.Replace(text, _path, null);
					}
					catch (Exception ex) when (ex is PlatformNotSupportedException || ex is IOException || ex is UnauthorizedAccessException)
					{
						File.Copy(text, _path, overwrite: true);
						File.Delete(text);
					}
				}
				else
				{
					File.Move(text, _path);
				}
				error = null;
				return true;
			}
			catch (Exception ex2)
			{
				error = ex2.ToString();
				try
				{
					if (File.Exists(text))
					{
						File.Delete(text);
					}
				}
				catch
				{
				}
				return false;
			}
		}

		private static DataContractJsonSerializer CreateSerializer()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			return new DataContractJsonSerializer(typeof(TeleportMarkerDatabase), new DataContractJsonSerializerSettings
			{
				UseSimpleDictionaryFormat = true
			});
		}

		private static void Normalize(TeleportMarkerDatabase database)
		{
			if (database.Players == null)
			{
				Dictionary<string, PlayerTeleportMarkers> dictionary = (database.Players = new Dictionary<string, PlayerTeleportMarkers>());
			}
			foreach (PlayerTeleportMarkers value in database.Players.Values)
			{
				PlayerTeleportMarkers current;
				PlayerTeleportMarkers playerTeleportMarkers = (current = value);
				if (current.Worlds == null)
				{
					Dictionary<string, WorldTeleportMarkers> dictionary3 = (current.Worlds = new Dictionary<string, WorldTeleportMarkers>());
				}
				foreach (WorldTeleportMarkers value2 in playerTeleportMarkers.Worlds.Values)
				{
					if (value2.Markers == null)
					{
						List<TeleportMarker> list = (value2.Markers = new List<TeleportMarker>());
					}
				}
			}
		}

		private static int CountMarkers(TeleportMarkerDatabase database)
		{
			int num = 0;
			foreach (PlayerTeleportMarkers value in database.Players.Values)
			{
				foreach (WorldTeleportMarkers value2 in value.Worlds.Values)
				{
					num += value2.Markers.Count;
				}
			}
			return num;
		}
	}
}
namespace AndersonDavid.UnrestrictedMineralPortals.Patches
{
	internal static class ChatAutoCompletePatch
	{
		private static readonly FieldInfo? InputField = AccessTools.Field(typeof(Terminal), "m_input");

		private static TeleportAutoCompleteController? _controller;

		internal static bool IsCompatible => (object)InputField != null;

		internal static void Initialize(TeleportAutoCompleteController controller)
		{
			_controller = controller;
		}

		internal static bool UpdateInputPrefix(Terminal __instance)
		{
			if (TeleportMarkerConfig.Enabled.Value && TeleportMarkerConfig.AutoCompleteEnabled.Value)
			{
				Chat val = (Chat)(object)((__instance is Chat) ? __instance : null);
				if (val != null && !((Object)(object)Chat.instance != (Object)(object)val) && val.HasFocus() && _controller != null)
				{
					object? obj = InputField?.GetValue(val);
					TMP_InputField val2 = (TMP_InputField)((obj is TMP_InputField) ? obj : null);
					if (val2 == null || !val2.isFocused || !ZInput.GetKeyDown((KeyCode)9, true))
					{
						return true;
					}
					bool reverse = ZInput.GetKey((KeyCode)304, true) || ZInput.GetKey((KeyCode)303, true);
					if (!_controller.TryComplete(val2.text, val2.caretPosition, reverse, out string completedText))
					{
						return true;
					}
					val2.text = completedText;
					val2.caretPosition = completedText.Length;
					val2.selectionAnchorPosition = completedText.Length;
					val2.selectionFocusPosition = completedText.Length;
					val2.ActivateInputField();
					return false;
				}
			}
			return true;
		}

		internal static void CancelPostfix()
		{
			_controller?.Cancel("chat closed or input submitted");
		}

		internal static void ChatUpdatePostfix(Chat __instance)
		{
			if ((Object)(object)Chat.instance == (Object)(object)__instance && !__instance.HasFocus())
			{
				_controller?.Cancel("chat lost focus");
			}
		}
	}
	internal static class ItemTeleportRestrictionIconPatch
	{
		internal static void TooltipPostfix(ref string __result)
		{
			__result = ItemRestrictionUiService.RemoveRestrictionTooltip(__result);
		}
	}
	internal static class PortalRestrictionPatch
	{
		internal static void Postfix(ref bool __result)
		{
			if (PortalRestrictionService.ShouldAllowTeleport())
			{
				if (!__result && ModConfig.DebugLogging.Value)
				{
					Plugin.Log.LogDebug((object)"Overriding Inventory.IsTeleportable result for portal travel.");
				}
				__result = true;
			}
		}
	}
}
namespace AndersonDavid.UnrestrictedMineralPortals.Models
{
	internal sealed class ConnectedPlayerInfo
	{
		internal ZDOID CharacterId { get; }

		internal string CharacterName { get; }

		internal string PlatformDisplayName { get; }

		internal string Name => CharacterName;

		internal ConnectedPlayerInfo(ZDOID characterId, string characterName, string platformDisplayName)
		{
			//IL_0007: 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)
			CharacterId = characterId;
			CharacterName = characterName;
			PlatformDisplayName = platformDisplayName;
		}
	}
	[DataContract]
	internal sealed class PlayerTeleportMarkers
	{
		[DataMember(Order = 1)]
		public Dictionary<string, WorldTeleportMarkers> Worlds { get; set; } = new Dictionary<string, WorldTeleportMarkers>();
	}
	[DataContract]
	internal sealed class TeleportMarker
	{
		[DataMember(Order = 1)]
		public string Name { get; set; } = string.Empty;

		[DataMember(Order = 2)]
		public float PositionX { get; set; }

		[DataMember(Order = 3)]
		public float PositionY { get; set; }

		[DataMember(Order = 4)]
		public float PositionZ { get; set; }

		[DataMember(Order = 5)]
		public float RotationX { get; set; }

		[DataMember(Order = 6)]
		public float RotationY { get; set; }

		[DataMember(Order = 7)]
		public float RotationZ { get; set; }

		[DataMember(Order = 8)]
		public float RotationW { get; set; }

		[DataMember(Order = 9)]
		public string WorldId { get; set; } = string.Empty;

		[DataMember(Order = 10)]
		public string PlayerId { get; set; } = string.Empty;

		[DataMember(Order = 11)]
		public DateTime CreatedAt { get; set; }

		[DataMember(Order = 13)]
		public long CreatedAtUnix { get; set; }
	}
	[DataContract]
	internal sealed class TeleportMarkerDatabase
	{
		[DataMember(Order = 1)]
		public Dictionary<string, PlayerTeleportMarkers> Players { get; set; } = new Dictionary<string, PlayerTeleportMarkers>();
	}
	[DataContract]
	internal sealed class WorldTeleportMarkers
	{
		[DataMember(Order = 1)]
		public List<TeleportMarker> Markers { get; set; } = new List<TeleportMarker>();
	}
}
namespace AndersonDavid.UnrestrictedMineralPortals.Localization
{
	internal static class EnglishMessages
	{
		internal static readonly IReadOnlyDictionary<string, string> Values = new Dictionary<string, string>
		{
			["command.invalid"] = "Invalid command. Use /tp help to view the commands.",
			["marker.empty"] = "You do not have any markers yet.\nUse /tp create markerName to create one.",
			["marker.disabled"] = "Teleport markers are disabled.",
			["marker.multiplayer_disabled"] = "Teleport markers are disabled on this server.",
			["marker.player_unavailable"] = "The local player or world is not available yet.",
			["marker.in_progress"] = "A teleport is already in progress.",
			["marker.save_error"] = "Markers could not be saved. Check LogOutput.log.",
			["command.internal_error"] = "An internal error occurred while processing the command.",
			["marker.created"] = "Marker \"{0}\" created successfully.\nPosition: {1}",
			["marker.duplicate"] = "A marker named \"{0}\" already exists.\nRemove it first with /tp delete {0}.",
			["marker.removed"] = "Marker \"{0}\" removed successfully.",
			["marker.not_found"] = "Marker \"{0}\" was not found.\nUse /tp list to view your markers.",
			["marker.other_world"] = "Marker \"{0}\" belongs to another world and cannot be used here.",
			["marker.limit"] = "You reached the limit of {0} markers in this world.",
			["marker.cooldown"] = "Wait {0} seconds before using another teleport.",
			["marker.teleported"] = "Teleported to \"{0}\".",
			["marker.list.header"] = "Your markers: {0}",
			["marker.other_world_count"] = "{0} marker(s) exist in other worlds.",
			["command.reserved_name"] = "The name \"{0}\" is reserved and cannot be used as a marker.",
			["command.help.title"] = "Teleport commands",
			["command.help.create"] = "/tp create markerName\nCreates a marker at the player's current position and altitude.",
			["command.help.teleport"] = "/tp markerName\nTeleports to an existing marker.",
			["command.help.delete"] = "/tp delete markerName\nDeletes a marker created by the current player.",
			["command.help.list"] = "/tp list\nLists all markers belonging to the player in the current world.",
			["command.help.help"] = "/tp help\nDisplays this command list.",
			["command.help.player"] = "/tp player playerName\nTeleports to another player connected to the same world.",
			["command.help.autocomplete_tip"] = "Tip: press Tab while typing a marker or player name to use autocomplete.",
			["teleport.player.success"] = "Teleported to player \"{0}\".",
			["teleport.player.not_found"] = "Player \"{0}\" was not found in this world.\nUse /tp player and press Tab to view available players.",
			["teleport.player.disconnected"] = "Player \"{0}\" is no longer connected.",
			["teleport.player.self"] = "You cannot teleport to yourself.",
			["teleport.player.none_connected"] = "There are no other players connected to this world.",
			["teleport.player.multiple_found"] = "Multiple players were found:\n\n{0}\n\nEnter a more specific name or press Tab to autocomplete.",
			["teleport.player.multiple_item"] = "- {0}",
			["teleport.player.name_required"] = "Enter the player's name.\n\n/tp player playerName",
			["teleport.player.invalid_position"] = "Could not obtain a valid position for player \"{0}\".",
			["teleport.player.destination_blocked"] = "Could not find a safe position near player \"{0}\".",
			["teleport.player.unavailable"] = "Teleporting to players is not available in this game version.",
			["teleport.player.feature_disabled"] = "Teleporting to players is disabled.",
			["teleport.player.position_unavailable"] = "The position of player \"{0}\" is not available yet. Try again in a few seconds.",
			["teleport.player.position_not_shared"] = "Player \"{0}\" is not sharing their position.",
			["teleport.player.duplicate_name"] = "Multiple players named \"{0}\" are connected. The destination could not be determined safely."
		};
	}
	internal static class LanguageCodeNormalizer
	{
		internal static string Configured(string? value)
		{
			string text = (value ?? string.Empty).Trim().Replace('_', '-').ToLowerInvariant();
			switch (text)
			{
			case "auto":
				return "auto";
			case "pt":
			case "pt-br":
			case "portuguese":
			case "portuguese-brazil":
			case "brazilianportuguese":
				return "pt-BR";
			default:
				if (!text.StartsWith("en-", StringComparison.Ordinal))
				{
					return string.Empty;
				}
				goto case "en";
			case "en":
			case "english":
				return "en";
			}
		}

		internal static string Game(string? value)
		{
			if (!(Configured(value) == "pt-BR"))
			{
				return "en";
			}
			return "pt-BR";
		}
	}
	internal static class LanguageManager
	{
		private static string _language = "en";

		private static bool _awaitingGameLanguage;

		private static float _nextAttempt;

		private static int _attempts;

		internal static string CurrentLanguage => _language;

		internal static void Initialize()
		{
			string text = ModConfig.Language.Value?.Trim() ?? "auto";
			string text2 = LanguageCodeNormalizer.Configured(text);
			if (text2 == "pt-BR" || text2 == "en")
			{
				Select(text2, fromGame: false);
				return;
			}
			if (!text.Equals("auto", StringComparison.OrdinalIgnoreCase))
			{
				Plugin.Log.LogWarning((object)("Unsupported configured language \"" + text + "\". Using game language detection."));
			}
			_language = "en";
			_awaitingGameLanguage = true;
			TryDetectGameLanguage();
		}

		internal static void Tick()
		{
			if (_awaitingGameLanguage && !(Time.unscaledTime < _nextAttempt))
			{
				_nextAttempt = Time.unscaledTime + 0.5f;
				if (TryDetectGameLanguage() || ++_attempts >= 20)
				{
					_awaitingGameLanguage = false;
				}
			}
		}

		internal static string Get(string key, params object[] arguments)
		{
			if (!((_language == "pt-BR") ? PortugueseBrazilMessages.Values : EnglishMessages.Values).TryGetValue(key, out string value))
			{
				value = key;
			}
			if (arguments.Length != 0)
			{
				return string.Format(CultureInfo.CurrentCulture, value, arguments);
			}
			return value;
		}

		internal static bool HaveLanguageParity()
		{
			return new HashSet<string>(PortugueseBrazilMessages.Values.Keys).SetEquals(EnglishMessages.Values.Keys);
		}

		private static bool TryDetectGameLanguage()
		{
			try
			{
				Type type = AccessTools.TypeByName("Localization");
				if ((object)type == null)
				{
					return false;
				}
				object obj = AccessTools.Property(type, "instance")?.GetValue(null, null) ?? AccessTools.Field(type, "instance")?.GetValue(null) ?? AccessTools.Field(type, "m_instance")?.GetValue(null);
				MethodInfo methodInfo = AccessTools.Method(type, "GetSelectedLanguage", Type.EmptyTypes, (Type[])null);
				if (obj == null || (object)methodInfo == null)
				{
					return false;
				}
				string value = methodInfo.Invoke(obj, null)?.ToString();
				if (string.IsNullOrWhiteSpace(value))
				{
					return false;
				}
				Select(LanguageCodeNormalizer.Game(value), fromGame: true);
				_awaitingGameLanguage = false;
				return true;
			}
			catch (Exception ex)
			{
				if (ModConfig.DebugLogging.Value)
				{
					Plugin.Log.LogDebug((object)("Game language detection is not ready: " + ex.Message));
				}
				return false;
			}
		}

		private static void Select(string language, bool fromGame)
		{
			_language = language;
			Plugin.Log.LogInfo((object)(fromGame ? ("Language selected from game settings: " + language + ".") : ("Language selected: " + language + ".")));
		}
	}
	internal static class MessageKeys
	{
		internal const string Invalid = "command.invalid";

		internal const string Empty = "marker.empty";

		internal const string Disabled = "marker.disabled";

		internal const string MultiplayerDisabled = "marker.multiplayer_disabled";

		internal const string PlayerUnavailable = "marker.player_unavailable";

		internal const string InProgress = "marker.in_progress";

		internal const string SaveError = "marker.save_error";

		internal const string InternalError = "command.internal_error";

		internal const string Created = "marker.created";

		internal const string Duplicate = "marker.duplicate";

		internal const string Removed = "marker.removed";

		internal const string NotFound = "marker.not_found";

		internal const string OtherWorld = "marker.other_world";

		internal const string Limit = "marker.limit";

		internal const string Cooldown = "marker.cooldown";

		internal const string Teleported = "marker.teleported";

		internal const string ListHeader = "marker.list.header";

		internal const string OtherWorldCount = "marker.other_world_count";

		internal const string ReservedName = "command.reserved_name";

		internal const string HelpTitle = "command.help.title";

		internal const string HelpCreate = "command.help.create";

		internal const string HelpTeleport = "command.help.teleport";

		internal const string HelpDelete = "command.help.delete";

		internal const string HelpList = "command.help.list";

		internal const string HelpHelp = "command.help.help";

		internal const string HelpAutocompleteTip = "command.help.autocomplete_tip";

		internal const string HelpPlayer = "command.help.player";

		internal const string PlayerSuccess = "teleport.player.success";

		internal const string PlayerNotFound = "teleport.player.not_found";

		internal const string PlayerDisconnected = "teleport.player.disconnected";

		internal const string PlayerSelf = "teleport.player.self";

		internal const string PlayerNone = "teleport.player.none_connected";

		internal const string PlayerMultiple = "teleport.player.multiple_found";

		internal const string PlayerMultipleItem = "teleport.player.multiple_item";

		internal const string PlayerNameRequired = "teleport.player.name_required";

		internal const string PlayerInvalidPosition = "teleport.player.invalid_position";

		internal const string PlayerDestinationBlocked = "teleport.player.destination_blocked";

		internal const string PlayerTeleportUnavailable = "teleport.player.unavailable";

		internal const string PlayerFeatureDisabled = "teleport.player.feature_disabled";

		internal const string PlayerPositionUnavailable = "teleport.player.position_unavailable";

		internal const string PlayerPositionNotShared = "teleport.player.position_not_shared";

		internal const string PlayerDuplicateName = "teleport.player.duplicate_name";
	}
	internal static class PortugueseBrazilMessages
	{
		internal static readonly IReadOnlyDictionary<string, string> Values = new Dictionary<string, string>
		{
			["command.invalid"] = "Comando inválido. Use /tp help para visualizar os comandos.",
			["marker.empty"] = "Você ainda não possui marcadores.\nUse /tp create nomeMarcador para criar um.",
			["marker.disabled"] = "Os marcadores de teletransporte estão desabilitados.",
			["marker.multiplayer_disabled"] = "Os marcadores de teletransporte estão desabilitados neste servidor.",
			["marker.player_unavailable"] = "O jogador local ou o mundo ainda não está disponível.",
			["marker.in_progress"] = "Já existe um teletransporte em andamento.",
			["marker.save_error"] = "Não foi possível salvar os marcadores. Consulte o LogOutput.log.",
			["command.internal_error"] = "Ocorreu um erro interno ao processar o comando.",
			["marker.created"] = "Marcador \"{0}\" criado com sucesso.\nPosição: {1}",
			["marker.duplicate"] = "Já existe um marcador chamado \"{0}\".\nRemova-o primeiro com /tp delete {0}.",
			["marker.removed"] = "Marcador \"{0}\" removido com sucesso.",
			["marker.not_found"] = "Marcador \"{0}\" não encontrado.\nUse /tp list para visualizar seus marcadores.",
			["marker.other_world"] = "O marcador \"{0}\" pertence a outro mundo e não pode ser usado aqui.",
			["marker.limit"] = "Você atingiu o limite de {0} marcadores neste mundo.",
			["marker.cooldown"] = "Aguarde {0} segundos para usar outro teletransporte.",
			["marker.teleported"] = "Teletransportado para \"{0}\".",
			["marker.list.header"] = "Seus marcadores: {0}",
			["marker.other_world_count"] = "{0} marcador(es) existem em outros mundos.",
			["command.reserved_name"] = "O nome \"{0}\" é reservado e não pode ser usado como marcador.",
			["command.help.title"] = "Comandos de teletransporte",
			["command.help.create"] = "/tp create nomeMarcador\nCria um marcador na posição e altitude atuais do jogador.",
			["command.help.teleport"] = "/tp nomeMarcador\nTeletransporta para um marcador existente.",
			["command.help.delete"] = "/tp delete nomeMarcador\nRemove um marcador criado pelo jogador atual.",
			["command.help.list"] = "/tp list\nLista todos os marcadores do jogador no mundo atual.",
			["command.help.help"] = "/tp help\nExibe esta lista de comandos.",
			["command.help.player"] = "/tp player nomeJogador\nTeletransporta até outro jogador conectado no mesmo mundo.",
			["command.help.autocomplete_tip"] = "Dica: pressione Tab enquanto digita o nome de um marcador ou jogador para usar o autocomplete.",
			["teleport.player.success"] = "Teletransportado até o jogador \"{0}\".",
			["teleport.player.not_found"] = "Jogador \"{0}\" não encontrado neste mundo.\nUse /tp player e pressione Tab para visualizar os jogadores disponíveis.",
			["teleport.player.disconnected"] = "O jogador \"{0}\" não está mais conectado.",
			["teleport.player.self"] = "Você não pode se teletransportar até si mesmo.",
			["teleport.player.none_connected"] = "Não existem outros jogadores conectados neste mundo.",
			["teleport.player.multiple_found"] = "Foram encontrados vários jogadores:\n\n{0}\n\nDigite um nome mais específico ou use Tab para completar.",
			["teleport.player.multiple_item"] = "- {0}",
			["teleport.player.name_required"] = "Informe o nome do jogador.\n\n/tp player nomeJogador",
			["teleport.player.invalid_position"] = "Não foi possível obter uma posição válida para o jogador \"{0}\".",
			["teleport.player.destination_blocked"] = "Não foi possível encontrar uma posição segura próxima ao jogador \"{0}\".",
			["teleport.player.unavailable"] = "O teletransporte até jogadores não está disponível nesta versão do jogo.",
			["teleport.player.feature_disabled"] = "O teletransporte até jogadores está desabilitado.",
			["teleport.player.position_unavailable"] = "A posição do jogador \"{0}\" ainda não está disponível. Tente novamente em alguns segundos.",
			["teleport.player.position_not_shared"] = "A posição do jogador \"{0}\" não está sendo compartilhada.",
			["teleport.player.duplicate_name"] = "Existem vários jogadores chamados \"{0}\". Não foi possível determinar o destino com segurança."
		};
	}
}
namespace AndersonDavid.UnrestrictedMineralPortals.Configuration
{
	internal static class ModConfig
	{
		internal static ConfigEntry<bool> Enabled { get; private set; }

		internal static ConfigEntry<bool> DebugLogging { get; private set; }

		internal static ConfigEntry<string> Language { get; private set; }

		internal static void Bind(ConfigFile config)
		{
			Enabled = config.Bind<bool>("General", "Enabled", true, "Allow restricted items through portals.");
			DebugLogging = config.Bind<bool>("General", "DebugLogging", false, "Enable diagnostic logging.");
			Language = config.Bind<string>("General", "Language", "auto", "Language used by mod messages. Supported values: auto, pt-BR, en.");
		}
	}
	internal static class TeleportMarkerConfig
	{
		internal static ConfigEntry<bool> Enabled { get; private set; }

		internal static ConfigEntry<int> MaxMarkersPerPlayer { get; private set; }

		internal static ConfigEntry<int> CooldownSeconds { get; private set; }

		internal static ConfigEntry<bool> AllowInMultiplayer { get; private set; }

		internal static ConfigEntry<bool> AutoCompleteEnabled { get; private set; }

		internal static ConfigEntry<int> AutoCompleteMaxSuggestions { get; private set; }

		internal static ConfigEntry<bool> AutoCompleteFuzzySearch { get; private set; }

		internal static ConfigEntry<bool> TeleportPlayersEnabled { get; private set; }

		internal static void Bind(ConfigFile config)
		{
			Enabled = config.Bind<bool>("TeleportMarkers", "Enabled", true, "Enable personal /tp marker commands.");
			MaxMarkersPerPlayer = config.Bind<int>("TeleportMarkers", "MaxMarkersPerPlayer", 50, "Maximum markers per player per world. Zero means unlimited.");
			CooldownSeconds = config.Bind<int>("TeleportMarkers", "CooldownSeconds", 3, "Seconds between marker teleports. Zero disables cooldown.");
			AllowInMultiplayer = config.Bind<bool>("TeleportMarkers", "AllowInMultiplayer", true, "Allow marker commands on remote multiplayer servers.");
			AutoCompleteEnabled = config.Bind<bool>("TeleportMarkers", "AutoCompleteEnabled", true, "Complete marker names with Tab in the normal chat.");
			AutoCompleteMaxSuggestions = config.Bind<int>("TeleportMarkers", "AutoCompleteMaxSuggestions", 20, "Maximum autocomplete results. Zero means unlimited.");
			AutoCompleteFuzzySearch = config.Bind<bool>("TeleportMarkers", "AutoCompleteFuzzySearch", true, "Suggest marker names for small typing mistakes.");
			TeleportPlayersEnabled = config.Bind<bool>("TeleportPlayers", "Enabled", true, "Enable teleporting to other connected players with /tp player playerName.");
		}
	}
}
namespace AndersonDavid.UnrestrictedMineralPortals.Commands
{
	internal static class NameMatcher
	{
		internal static List<T> Match<T>(IEnumerable<T> values, Func<T, string> name, string query, bool fuzzy, int maximum)
		{
			string normalizedQuery = Normalize(query);
			double similarity;
			IEnumerable<T> source = from x in (from x in values
					select new
					{
						Value = x,
						Name = name(x),
						Normalized = Normalize(name(x))
					} into x
					select new
					{
						Value = x.Value,
						Name = x.Name,
						Normalized = x.Normalized,
						Rank = Rank(x.Normalized, normalizedQuery, fuzzy, out similarity),
						Similarity = similarity
					} into x
					where x.Rank >= 0
					orderby x.Rank, x.Similarity descending
					select x).ThenBy(x => x.Normalized, StringComparer.Ordinal)
				select x.Value;
			if (maximum <= 0)
			{
				return source.ToList();
			}
			return source.Take(maximum).ToList();
		}

		internal static string Normalize(string value)
		{
			string text = (value ?? string.Empty).Trim().Normalize(NormalizationForm.FormD);
			StringBuilder stringBuilder = new StringBuilder(text.Length);
			bool flag = false;
			string text2 = text;
			foreach (char c in text2)
			{
				if (CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.NonSpacingMark)
				{
					continue;
				}
				if (char.IsWhiteSpace(c))
				{
					flag = stringBuilder.Length > 0;
					continue;
				}
				if (flag)
				{
					stringBuilder.Append(' ');
					flag = false;
				}
				stringBuilder.Append(char.ToLowerInvariant(c));
			}
			return stringBuilder.ToString().Normalize(NormalizationForm.FormC);
		}

		private static int Rank(string name, string query, bool fuzzy, out double similarity)
		{
			similarity = 1.0;
			if (query.Length == 0)
			{
				return 1;
			}
			if (name == query)
			{
				return 0;
			}
			if (name.StartsWith(query, StringComparison.Ordinal))
			{
				return 1;
			}
			if (name.Split(new char[1] { ' ' }).Any((string word) => word.StartsWith(query, StringComparison.Ordinal)))
			{
				return 2;
			}
			if (name.Contains(query))
			{
				return 3;
			}
			if (!fuzzy)
			{
				return -1;
			}
			similarity = Similarity(name, query);
			if (!(similarity >= 0.72))
			{
				return -1;
			}
			return 4;
		}

		private static double Similarity(string left, string right)
		{
			if (left.Length == 0 || right.Length == 0)
			{
				return 0.0;
			}
			int[] array = new int[right.Length + 1];
			int[] array2 = new int[right.Length + 1];
			for (int i = 0; i <= right.Length; i++)
			{
				array[i] = i;
			}
			for (int j = 1; j <= left.Length; j++)
			{
				array2[0] = j;
				for (int k = 1; k <= right.Length; k++)
				{
					array2[k] = Math.Min(Math.Min(array2[k - 1] + 1, array[k] + 1), array[k - 1] + ((left[j - 1] != right[k - 1]) ? 1 : 0));
				}
				int[] array3 = array;
				array = array2;
				array2 = array3;
			}
			return 1.0 - (double)array[right.Length] / (double)Math.Max(left.Length, right.Length);
		}
	}
	internal sealed class PlayerAutoCompleteSession
	{
		internal string Prefix { get; set; } = string.Empty;

		internal List<ConnectedPlayerInfo> Suggestions { get; set; } = new List<ConnectedPlayerInfo>();

		internal int Index { get; set; }

		internal string CompletedText { get; set; } = string.Empty;

		internal string WorldId { get; set; } = string.Empty;

		internal string ConnectionSignature { get; set; } = string.Empty;
	}
	internal enum PlayerNameResolutionStatus
	{
		Found,
		NotFound,
		Ambiguous
	}
	internal sealed class PlayerNameResolution<T>
	{
		internal PlayerNameResolutionStatus Status { get; set; }

		internal T? Value { get; set; }

		internal IReadOnlyList<T> Candidates { get; set; } = Array.Empty<T>();
	}
	internal static class PlayerNameResolver
	{
		internal static PlayerNameResolution<T> Resolve<T>(IEnumerable<T> values, Func<T, string> name, string query)
		{
			List<T> list = values.ToList();
			List<T> list2 = list.Where((T x) => name(x) == query).ToList();
			if (list2.Count == 1)
			{
				return Found(list2[0]);
			}
			List<T> list3 = list.Where((T x) => name(x).Equals(query, StringComparison.OrdinalIgnoreCase)).ToList();
			if (list3.Count == 1)
			{
				return Found(list3[0]);
			}
			string normalized = NameMatcher.Normalize(query);
			List<T> list4 = list.Where((T x) => NameMatcher.Normalize(name(x)) == normalized).ToList();
			if (list4.Count == 1)
			{
				return Found(list4[0]);
			}
			List<T> list5 = NameMatcher.Match(list, name, query, fuzzy: false, 0);
			if (list5.Count == 1)
			{
				return Found(list5[0]);
			}
			return new PlayerNameResolution<T>
			{
				Status = ((list5.Count <= 1) ? PlayerNameResolutionStatus.NotFound : PlayerNameResolutionStatus.Ambiguous),
				Candidates = list5
			};
		}

		private static PlayerNameResolution<T> Found<T>(T value)
		{
			PlayerNameResolution<T> playerNameResolution = new PlayerNameResolution<T>();
			playerNameResolution.Status = PlayerNameResolutionStatus.Found;
			playerNameResolution.Value = value;
			playerNameResolution.Candidates = new T[1] { value };
			return playerNameResolution;
		}
	}
	internal enum TeleportAutoCompleteMode
	{
		None,
		Teleport,
		Delete,
		Player
	}
	internal sealed class TeleportAutoCompleteContext
	{
		internal TeleportAutoCompleteMode Mode { get; set; }

		internal string Prefix { get; set; } = string.Empty;

		internal string Query { get; set; } = string.Empty;

		internal bool IsEnabled => Mode != TeleportAutoCompleteMode.None;
	}
	internal static class TeleportAutoCompleteContextParser
	{
		internal static TeleportAutoCompleteContext Parse(string? text)
		{
			if (string.IsNullOrEmpty(text) || text.Length < 3 || text[0] != '/' || !text.Substring(1, 2).Equals("tp", StringComparison.OrdinalIgnoreCase) || (text.Length > 3 && !char.IsWhiteSpace(text[3])))
			{
				return None();
			}
			int i;
			for (i = 3; i < text.Length && char.IsWhiteSpace(text[i]); i++)
			{
			}
			if (i >= text.Length)
			{
				return Enabled(TeleportAutoCompleteMode.Teleport, EnsureTrailingSpace(text), string.Empty);
			}
			string text2 = text.Substring(i);
			if (StartsOperation(text2, "create", out var afterOperation))
			{
				return None();
			}
			if (StartsOperation(text2, "list", out afterOperation) || StartsOperation(text2, "help", out afterOperation))
			{
				return None();
			}
			if (StartsOperation(text2, "player", out var afterOperation2))
			{
				int j;
				for (j = i + afterOperation2; j < text.Length && char.IsWhiteSpace(text[j]); j++)
				{
				}
				string prefix = ((j < text.Length) ? text.Substring(0, j) : EnsureTrailingSpace(text));
				return Enabled(TeleportAutoCompleteMode.Player, prefix, (j < text.Length) ? text.Substring(j) : string.Empty);
			}
			if (StartsOperation(text2, "delete", out var afterOperation3))
			{
				int k;
				for (k = i + afterOperation3; k < text.Length && char.IsWhiteSpace(text[k]); k++)
				{
				}
				string prefix2 = ((k < text.Length) ? text.Substring(0, k) : EnsureTrailingSpace(text));
				return Enabled(TeleportAutoCompleteMode.Delete, prefix2, (k < text.Length) ? text.Substring(k) : string.Empty);
			}
			return Enabled(TeleportAutoCompleteMode.Teleport, text.Substring(0, i), text2);
		}

		private static bool StartsOperation(string text, string operation, out int afterOperation)
		{
			afterOperation = operation.Length;
			if (!text.Equals(operation, StringComparison.OrdinalIgnoreCase))
			{
				if (text.Length > operation.Length && text.StartsWith(operation, StringComparison.OrdinalIgnoreCase))
				{
					return char.IsWhiteSpace(text[operation.Length]);
				}
				return false;
			}
			return true;
		}

		private static string EnsureTrailingSpace(string text)
		{
			if (text.Length <= 0 || !char.IsWhiteSpace(text[text.Length - 1]))
			{
				return text + " ";
			}
			return text;
		}

		private static TeleportAutoCompleteContext None()
		{
			return new TeleportAutoCompleteContext();
		}

		private static TeleportAutoCompleteContext Enabled(TeleportAutoCompleteMode mode, string prefix, string query)
		{
			return new TeleportAutoCompleteContext
			{
				Mode = mode,
				Prefix = prefix,
				Query = query
			};
		}
	}
	internal sealed class TeleportAutoCompleteController
	{
		private readonly TeleportMarkerService _markers;

		private readonly ConnectedPlayerService _players;

		private TeleportAutoCompleteSession? _session;

		private PlayerAutoCompleteSession? _playerSession;

		internal TeleportAutoCompleteController(TeleportMarkerService markers, ConnectedPlayerService players)
		{
			_markers = markers;
			_players = players;
		}

		internal bool TryComplete(string text, int caretPosition, bool reverse, out string completedText)
		{
			completedText = text;
			if (caretPosition != text.Length || !PlayerIdentityService.TryGet(out string playerId) || !WorldIdentityService.TryGet(out string worldId))
			{
				Cancel("cursor or game context changed");
				return false;
			}
			TeleportAutoCompleteContext teleportAutoCompleteContext = TeleportAutoCompleteContextParser.Parse(text);
			if (teleportAutoCompleteContext.Mode == TeleportAutoCompleteMode.Player)
			{
				return TryCompletePlayer(text, worldId, teleportAutoCompleteContext, reverse, out completedText);
			}
			_playerSession = null;
			if (_session != null && _session.CompletedText == text && _session.PlayerId == playerId && _session.WorldId == worldId && _session.MarkerRevision == _markers.Revision)
			{
				_session.Index = Wrap(_session.Index + ((!reverse) ? 1 : (-1)), _session.Suggestions.Count);
				completedText = _session.Prefix + _session.Suggestions[_session.Index].Name;
				_session.CompletedText = completedText;
				DebugSelection();
				return true;
			}
			Cancel("text, world, player, or marker list changed");
			if (!teleportAutoCompleteContext.IsEnabled)
			{
				return false;
			}
			List<TeleportMarker> list = TeleportMarkerMatcher.Match(_markers.List(playerId, worldId), teleportAutoCompleteContext.Query, TeleportMarkerConfig.AutoCompleteFuzzySearch.Value, Math.Max(0, TeleportMarkerConfig.AutoCompleteMaxSuggestions.Value));
			if (list.Count == 0)
			{
				return false;
			}
			int index = (reverse ? (list.Count - 1) : 0);
			completedText = teleportAutoCompleteContext.Prefix + list[index].Name;
			_session = new TeleportAutoCompleteSession
			{
				OriginalQuery = teleportAutoCompleteContext.Query,
				Prefix = teleportAutoCompleteContext.Prefix,
				Suggestions = list,
				Index = index,
				CompletedText = completedText,
				PlayerId = playerId,
				WorldId = worldId,
				MarkerRevision = _markers.Revision
			};
			if (ModConfig.DebugLogging.Value)
			{
				Plugin.Log.LogDebug((object)string.Format("Autocomplete session started: query='{0}', suggestions={1} [{2}].", teleportAutoCompleteContext.Query, list.Count, string.Join(", ", list.Select((TeleportMarker x) => x.Name))));
			}
			DebugSelection();
			return true;
		}

		internal void Cancel(string reason)
		{
			if (_session != null && ModConfig.DebugLogging.Value)
			{
				Plugin.Log.LogDebug((object)("Autocomplete session ended: " + reason + "."));
			}
			_session = null;
			_playerSession = null;
		}

		private void DebugSelection()
		{
			if (_session != null && ModConfig.DebugLogging.Value)
			{
				Plugin.Log.LogDebug((object)$"Autocomplete selected {_session.Index + 1}/{_session.Suggestions.Count}: {_session.Suggestions[_session.Index].Name}.");
			}
		}

		private static int Wrap(int value, int count)
		{
			return (value % count + count) % count;
		}

		private bool TryCompletePlayer(string text, string worldId, TeleportAutoCompleteContext context, bool reverse, out string completed)
		{
			completed = text;
			List<ConnectedPlayerInfo> list = _players.ListOthers();
			string text2 = string.Join("|", list.Select((ConnectedPlayerInfo x) => ((object)x.CharacterId/*cast due to .constrained prefix*/).ToString() + ":" + x.Name));
			if (_playerSession != null && _playerSession.CompletedText == text && _playerSession.WorldId == worldId && _playerSession.ConnectionSignature == text2)
			{
				_playerSession.Index = Wrap(_playerSession.Index + ((!reverse) ? 1 : (-1)), _playerSession.Suggestions.Count);
				completed = _playerSession.Prefix + _playerSession.Suggestions[_playerSession.Index].Name;
				_playerSession.CompletedText = completed;
				return true;
			}
			_playerSession = null;
			List<ConnectedPlayerInfo> list2 = NameMatcher.Match(list, (ConnectedPlayerInfo x) => x.Name, context.Query, TeleportMarkerConfig.AutoCompleteFuzzySearch.Value, Math.Max(0, TeleportMarkerConfig.AutoCompleteMaxSuggestions.Value));
			if (list2.Count == 0)
			{
				return false;
			}
			int index = (reverse ? (list2.Count - 1) : 0);
			completed = context.Prefix + list2[index].Name;
			_playerSession = new PlayerAutoCompleteSession
			{
				Prefix = context.Prefix,
				Suggestions = list2,
				Index = index,
				CompletedText = completed,
				WorldId = worldId,
				ConnectionSignature = text2
			};
			if (ModConfig.DebugLogging.Value)
			{
				Plugin.Log.LogDebug((object)$"Player autocomplete query='{context.Query}', matches={list2.Count}, selected='{list2[index].Name}'.");
			}
			return true;
		}
	}
	internal sealed class TeleportAutoCompleteSession
	{
		internal string OriginalQuery { get; set; } = string.Empty;

		internal string Prefix { get; set; } = string.Empty;

		internal List<TeleportMarker> Suggestions { get; set; } = new List<TeleportMarker>();

		internal int Index { get; set; }

		internal string CompletedText { get; set; } = string.Empty;

		internal string PlayerId { get; set; } = string.Empty;

		internal string WorldId { get; set; } = string.Empty;

		internal int MarkerRevision { get; set; }
	}
	internal sealed class TeleportCommandHandler
	{
		private readonly TeleportMarkerService _markers;

		private readonly TeleportService _teleport;

		private readonly ConnectedPlayerService _connectedPlayers;

		private readonly PlayerTeleportService _playerTeleport;

		internal TeleportCommandHandler(TeleportMarkerService markers, TeleportService teleport, ConnectedPlayerService connectedPlayers)
		{
			_markers = markers;
			_teleport = teleport;
			_connectedPlayers = connectedPlayers;
			_playerTeleport = new PlayerTeleportService(connectedPlayers, teleport);
		}

		internal void Handle(ConsoleEventArgs args)
		{
			try
			{
				if (!TeleportMarkerConfig.Enabled.Value)
				{
					Reply(args, TeleportMessages.Disabled);
					return;
				}
				if (!TeleportMarkerConfig.AllowInMultiplayer.Value && !ZNet.IsSinglePlayer && ZNet.GetWorldIfIsHost() == null)
				{
					Reply(args, TeleportMessages.MultiplayerDisabled);
					return;
				}
				TeleportCommandResult teleportCommandResult = TeleportCommandParser.Parse(args.FullLine);
				if (teleportCommandResult.Operation == TeleportCommandOperation.Invalid)
				{
					Reply(args, TeleportMessages.Invalid);
					return;
				}
				if (teleportCommandResult.Operation == TeleportCommandOperation.Help)
				{
					Reply(args, TeleportMessages.Help(TeleportMarkerConfig.AutoCompleteEnabled.Value));
					return;
				}
				if (!TryContext(out Player player, out string playerId, out string worldId))
				{
					Reply(args, TeleportMessages.PlayerUnavailable);
					return;
				}
				if (ModConfig.DebugLogging.Value)
				{
					Plugin.Log.LogDebug((object)$"Teleport marker command identified: operation={teleportCommandResult.Operation}, world loaded, player available.");
				}
				switch (teleportCommandResult.Operation)
				{
				case TeleportCommandOperation.Create:
					Create(args, player, playerId, worldId, teleportCommandResult.MarkerName);
					break;
				case TeleportCommandOperation.Delete:
					Delete(args, playerId, worldId, teleportCommandResult.MarkerName);
					break;
				case TeleportCommandOperation.List:
					List(args, playerId, worldId);
					break;
				case TeleportCommandOperation.Teleport:
					Teleport(args, player, playerId, worldId, teleportCommandResult.MarkerName);
					break;
				case TeleportCommandOperation.Player:
					TeleportPlayer(args, player, playerId, teleportCommandResult.MarkerName);
					break;
				default:
					Reply(args, TeleportMessages.Invalid);
					break;
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Teleport marker command failed.\n{arg}");
				Reply(args, TeleportMessages.InternalError);
			}
		}

		private void Create(ConsoleEventArgs args, Player player, string playerId, string worldId, string rawName)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: 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_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			string name = TeleportMarkerService.NormalizeName(rawName);
			if (TeleportCommandParser.IsReservedName(name))
			{
				Reply(args, TeleportMessages.ReservedName(name));
				return;
			}
			Vector3 position = ((Component)player).transform.position;
			Quaternion rotation = ((Component)player).transform.rotation;
			TeleportMarker marker = new TeleportMarker
			{
				Name = name,
				PlayerId = playerId,
				WorldId = worldId,
				CreatedAt = DateTime.UtcNow,
				CreatedAtUnix = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
				PositionX = position.x,
				PositionY = position.y,
				PositionZ = position.z,
				RotationX = rotation.x,
				RotationY = rotation.y,
				RotationZ = rotation.z,
				RotationW = rotation.w
			};
			int num = Math.Max(0, TeleportMarkerConfig.MaxMarkersPerPlayer.Value);
			MarkerOperationResult markerOperationResult = _markers.Create(marker, num);
			switch (markerOperationResult.Status)
			{
			case MarkerOperationStatus.Success:
				Reply(args, TeleportMessages.Created(marker));
				break;
			case MarkerOperationStatus.Duplicate:
				Reply(args, TeleportMessages.Duplicate(name));
				break;
			case MarkerOperationStatus.LimitReached:
				Reply(args, TeleportMessages.Limit(num));
				break;
			default:
				LogSaveError(markerOperationResult.Error);
				Reply(args, TeleportMessages.SaveError);
				break;
			}
		}

		private void Delete(ConsoleEventArgs args, string playerId, string worldId, string name)
		{
			name = TeleportMarkerService.NormalizeName(name);
			MarkerOperationResult markerOperationResult = _markers.Delete(playerId, worldId, name);
			if (markerOperationResult.Status == MarkerOperationStatus.Success)
			{
				Reply(args, TeleportMessages.Removed(name));
				return;
			}
			if (markerOperationResult.Status == MarkerOperationStatus.NotFound)
			{
				Reply(args, TeleportMessages.NotFound(name));
				return;
			}
			LogSaveError(markerOperationResult.Error);
			Reply(args, TeleportMessages.SaveError);
		}

		private void List(ConsoleEventArgs args, string playerId, string worldId)
		{
			List<TeleportMarker> list = _markers.List(playerId, worldId);
			Reply(args, (list.Count == 0) ? TeleportMessages.Empty : TeleportMessages.List(list, _markers.CountOtherWorlds(playerId, worldId)));
		}

		private void Teleport(ConsoleEventArgs args, Player player, string playerId, string worldId, string name)
		{
			name = TeleportMarkerService.NormalizeName(name);
			TeleportMarker teleportMarker = _markers.Find(playerId, worldId, name);
			if (teleportMarker == null)
			{
				Reply(args, (_markers.FindInOtherWorld(playerId, worldId, name) == null) ? TeleportMessages.NotFound(name) : TeleportMessages.OtherWorld(name));
				return;
			}
			TeleportAttemptResult teleportAttemptResult = _teleport.Teleport(player, playerId, teleportMarker, Math.Max(0, TeleportMarkerConfig.CooldownSeconds.Value));
			switch (teleportAttemptResult.Status)
			{
			case TeleportAttemptStatus.Success:
				Reply(args, TeleportMessages.Teleported(teleportMarker.Name));
				break;
			case TeleportAttemptStatus.Cooldown:
				Reply(args, TeleportMessages.Cooldown(teleportAttemptResult.RemainingSeconds));
				break;
			case TeleportAttemptStatus.InProgress:
				Reply(args, TeleportMessages.TeleportInProgress);
				break;
			case TeleportAttemptStatus.PlayerUnavailable:
				Reply(args, TeleportMessages.PlayerUnavailable);
				break;
			default:
				Reply(args, TeleportMessages.InternalError);
				break;
			}
		}

		private static bool TryContext(out Player? player, out string playerId, out string worldId)
		{
			playerId = string.Empty;
			worldId = string.Empty;
			player = Player.m_localPlayer;
			if (player != null && PlayerIdentityService.TryGet(out playerId))
			{
				return WorldIdentityService.TryGet(out worldId);
			}
			return false;
		}

		private void TeleportPlayer(ConsoleEventArgs args, Player local, string playerId, string query)
		{
			query = TeleportMarkerService.NormalizeName(query);
			if (!TeleportMarkerConfig.TeleportPlayersEnabled.Value)
			{
				Reply(args, TeleportMessages.Player("teleport.player.feature_disabled"));
				return;
			}
			if (query.Length == 0)
			{
				Reply(args, TeleportMessages.Player("teleport.player.name_required"));
				return;
			}
			if (!_connectedPlayers.IsAvailable)
			{
				Reply(args, TeleportMessages.Player("teleport.player.unavailable"));
				return;
			}
			if (NameMatcher.Normalize(local.GetPlayerName()) == NameMatcher.Normalize(query))
			{
				Reply(args, TeleportMessages.Player("teleport.player.self"));
				return;
			}
			List<ConnectedPlayerInfo> list = _connectedPlayers.ListOthers();
			if (list.Count == 0)
			{
				Reply(args, TeleportMessages.Player("teleport.player.none_connected"));
				return;
			}
			PlayerNameResolution<ConnectedPlayerInfo> playerNameResolution = PlayerNameResolver.Resolve(list, (ConnectedPlayerInfo x) => x.Name, query);
			if (ModConfig.DebugLogging.Value)
			{
				Plugin.Log.LogDebug((object)$"Player teleport query='{query}', matches={playerNameResolution.Candidates.Count}.");
			}
			if (playerNameResolution.Status == PlayerNameResolutionStatus.NotFound)
			{
				Reply(args, TeleportMessages.Player("teleport.player.not_found", query));
				return;
			}
			if (playerNameResolution.Status == PlayerNameResolutionStatus.Ambiguous)
			{
				if (playerNameResolution.Candidates.Count > 1 && playerNameResolution.Candidates.All((ConnectedPlayerInfo x) => NameMatcher.Normalize(x.Name) == NameMatcher.Normalize(query)))
				{
					Reply(args, TeleportMessages.Player("teleport.player.duplicate_name", query));
					return;
				}
				string text = string.Join("\n", playerNameResolution.Candidates.Select((ConnectedPlayerInfo x) => TeleportMessages.Player("teleport.player.multiple_item", x.Name)));
				Reply(args, TeleportMessages.Player("teleport.player.multiple_found", text));
				return;
			}
			ConnectedPlayerInfo value = playerNameResolution.Value;
			PlayerTeleportResult playerTeleportResult = _playerTeleport.Teleport(local, playerId, value, Math.Max(0, TeleportMarkerConfig.CooldownSeconds.Value));
			switch (playerTeleportResult.Status)
			{
			case PlayerTeleportStatus.Success:
				Reply(args, TeleportMessages.Player("teleport.player.success", value.Name));
				break;
			case PlayerTeleportStatus.Disconnected:
				Reply(args, TeleportMessages.Player("teleport.player.disconnected", value.Name));
				break;
			case PlayerTeleportStatus.PositionUnavailable:
				Reply(args, TeleportMessages.Player("teleport.player.position_unavailable", value.Name));
				break;
			case PlayerTeleportStatus.PositionNotShared:
				Reply(args, TeleportMessages.Player("teleport.player.position_not_shared", value.Name));
				break;
			case PlayerTeleportStatus.DestinationBlocked:
				Reply(args, TeleportMessages.Player("teleport.player.destination_blocked", value.Name));
				break;
			case PlayerTeleportStatus.Cooldown:
				Reply(args, TeleportMessages.Cooldown(playerTeleportResult.RemainingSeconds));
				break;
			case PlayerTeleportStatus.InProgress:
				Reply(args, TeleportMessages.TeleportInProgress);
				break;
			case PlayerTeleportStatus.PlayerUnavailable:
				Reply(args, TeleportMessages.PlayerUnavailable);
				break;
			default:
				Reply(args, TeleportMessages.InternalError);
				break;
			}
		}

		private static void Reply(ConsoleEventArgs args, string text)
		{
			Terminal context = args.Context;
			if (context != null)
			{
				context.AddString(text);
			}
		}

		private static void LogSaveError(string? error)
		{
			Plugin.Log.LogError((object)("Failed to save teleport markers: " + error));
		}
	}
	internal static class TeleportCommandParser
	{
		internal static bool HasExactPrefix(string? input)
		{
			if (string.IsNullOrWhiteSpace(input))
			{
				return false;
			}
			string text = input.TrimStart(Array.Empty<char>());
			if (text.StartsWith("/", StringComparison.Ordinal))
			{
				text = text.Substring(1);
			}
			if (!text.Equals("tp", StringComparison.OrdinalIgnoreCase))
			{
				if (text.Length > 3 && text.StartsWith("tp", StringComparison.OrdinalIgnoreCase))
				{
					return char.IsWhiteSpace(text[2]);
				}
				return false;
			}
			return true;
		}

		internal static TeleportCommandResult Parse(string? input)
		{
			if (!HasExactPrefix(input))
			{
				return Invalid();
			}
			string text = input.TrimStart(Array.Empty<char>());
			if (text[0] == '/')
			{
				text = text.Substring(1);
			}
			string text2 = ((text.Length == 2) ? string.Empty : text.Substring(2).Trim());
			if (text2.Length == 0)
			{
				return Invalid();
			}
			if (TryOperation(text2, "create", out string name))
			{
				if (name.Length != 0)
				{
					return Result(TeleportCommandOperation.Create, name);
				}
				return Invalid();
			}
			if (TryOperation(text2, "delete", out name))
			{
				if (name.Length != 0)
				{
					return Result(TeleportCommandOperation.Delete, name);
				}
				return Invalid();
			}
			if (text2.Equals("list", StringComparison.OrdinalIgnoreCase))
			{
				return Result(TeleportCommandOperation.List, string.Empty);
			}
			if (TryOperation(text2, "list", out string name2))
			{
				return Invalid();
			}
			if (text2.Equals("help", StringComparison.OrdinalIgnoreCase))
			{
				return Result(TeleportCommandOperation.Help, string.Empty);
			}
			if (TryOperation(text2, "help", out name2))
			{
				return Invalid();
			}
			if (TryOperation(text2, "player", out name))
			{
				return Result(TeleportCommandOperation.Player, name);
			}
			return Result(TeleportCommandOperation.Teleport, text2);
		}

		internal static bool IsReservedName(string name)
		{
			if (!name.Equals("create", StringComparison.OrdinalIgnoreCase) && !name.Equals("delete", StringComparison.OrdinalIgnoreCase) && !name.Equals("list", StringComparison.OrdinalIgnoreCase) && !name.Equals("help", StringComparison.OrdinalIgnoreCase))
			{
				return name.Equals("player", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		private static bool TryOperation(string input, string operation, out string name)
		{
			if (input.Equals(operation, StringComparison.OrdinalIgnoreCase))
			{
				name = string.Empty;
				return true;
			}
			if (input.Length > operation.Length && input.StartsWith(operation, StringComparison.OrdinalIgnoreCase) && char.IsWhiteSpace(input[operation.Length]))
			{
				name = input.Substring(operation.Length).Trim();
				return true;
			}
			name = string.Empty;
			return false;
		}

		private static TeleportCommandResult Invalid()
		{
			return Result(TeleportCommandOperation.Invalid, string.Empty);
		}

		private static TeleportCommandResult Result(TeleportCommandOperation operation, string name)
		{
			return new TeleportCommandResult
			{
				Operation = operation,
				MarkerName = name
			};
		}
	}
	internal enum TeleportCommandOperation
	{
		Invalid,
		Create,
		Delete,
		List,
		Help,
		Player,
		Teleport
	}
	internal sealed class TeleportCommandResult
	{
		internal TeleportCommandOperation Operation { get; set; }

		internal string MarkerName { get; set; } = string.Empty;
	}
	internal static class TeleportMarkerMatcher
	{
		internal static List<TeleportMarker> Match(IEnumerable<TeleportMarker> markers, string query, bool fuzzy, int maximum)
		{
			return NameMatcher.Match(markers, (TeleportMarker x) => x.Name, query, fuzzy, maximum);
		}

		internal static string Normalize(string value)
		{
			return NameMatcher.Normalize(value);
		}
	}
	internal static class TeleportMessages
	{
		internal static string Invalid => Get("command.invalid");

		internal static string Empty => Get("marker.empty");

		internal static string Disabled => Get("marker.disabled");

		internal static string MultiplayerDisabled => Get("marker.multiplayer_disabled");

		internal static string PlayerUnavailable => Get("marker.player_unavailable");

		internal static string TeleportInProgress => Get("marker.in_progress");

		internal static string SaveError => Get("marker.save_error");

		internal static string InternalError => Get("command.internal_error");

		internal static string Created(TeleportMarker marker)
		{
			return Get("marker.created", marker.Name, Coordinates(marker));
		}

		internal static string Duplicate(string name)
		{
			return Get("marker.duplicate", name);
		}

		internal static string Removed(string name)
		{
			return Get("marker.removed", name);
		}

		internal static string NotFound(string name)
		{
			return Get("marker.not_found", name);
		}

		internal static string OtherWorld(string name)
		{
			return Get("marker.other_world", name);
		}

		internal static string Limit(int value)
		{
			return Get("marker.limit", value);
		}

		internal static string Cooldown(int seconds)
		{
			return Get("marker.cooldown", seconds);
		}

		internal static string Teleported(string name)
		{
			return Get("marker.teleported", name);
		}

		internal static string ReservedName(string name)
		{
			return Get("command.reserved_name", name);
		}

		internal static string Help(bool autocomplete)
		{
			string text = string.Join("\n\n", Get("command.help.title"), Get("command.help.create"), Get("command.help.teleport"), Get("command.help.player"), Get("command.help.delete"), Get("command.help.list"), Get("command.help.help"));
			if (!autocomplete)
			{
				return text;
			}
			return text + "\n\n" + Get("command.help.autocomplete_tip");
		}

		internal static string List(IReadOnlyList<TeleportMarker> markers, int otherWorldCount)
		{
			StringBuilder stringBuilder = new StringBuilder(Get("marker.list.header", markers.Count));
			for (int i = 0; i < markers.Count; i++)
			{
				stringBuilder.Append($"\n\n{i + 1}. {markers[i].Name} — {Coordinates(markers[i])}");
			}
			if (otherWorldCount > 0)
			{
				stringBuilder.Append("\n\n" + Get("marker.other_world_count", otherWorldCount));
			}
			return stringBuilder.ToString();
		}

		private static string Coordinates(TeleportMarker marker)
		{
			return string.Format(CultureInfo.CurrentCulture, "X: {0:F2}, Y: {1:F2}, Z: {2:F2}", marker.PositionX, marker.PositionY, marker.PositionZ);
		}

		private static string Get(string key, params object[] values)
		{
			return LanguageManager.Get(key, values);
		}

		internal static string Player(string key, params object[] values)
		{
			return Get(key, values);
		}
	}
}