Decompiled source of ValheimRaftBedFix v0.1.5

BepInEx/plugins/ValheimRaftBedFix/ValheimRaftBedFix.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ValheimRaftBedFix")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.5.0")]
[assembly: AssemblyInformationalVersion("0.1.5")]
[assembly: AssemblyProduct("ValheimRaftBedFix")]
[assembly: AssemblyTitle("ValheimRaftBedFix")]
[assembly: AssemblyVersion("0.1.5.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ValheimRaftBedFix
{
	public static class MobileShipDetector
	{
		public const string ShipGuidZdoKey = "ValheimRaftBedFix_ShipGuid";

		private const string ShipRegistryPositionZdoKey = "ValheimRaftBedFix_ShipRegistryPosition";

		private const string ShipRegistryZoneZdoKey = "ValheimRaftBedFix_ShipRegistryZone";

		private const string ShipRegistryUpdatedZdoKey = "ValheimRaftBedFix_ShipRegistryUpdated";

		private const float NearbySearchRadius = 8f;

		private const int MinimumVehicleScore = 100;

		public static bool TryFindVehicleRoot(Player player, out Transform vehicleRoot, out ZNetView vehicleView)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			vehicleRoot = null;
			vehicleView = null;
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			Ship componentInParent = ((Component)player).GetComponentInParent<Ship>();
			if ((Object)(object)componentInParent != (Object)null && TryGetValidView(((Component)componentInParent).transform, out vehicleView))
			{
				vehicleRoot = ((Component)componentInParent).transform;
				RegisterShip(vehicleView, vehicleRoot);
				return true;
			}
			if (TryFindBestVehicleRootFromTransform(((Component)player).transform, ((Component)player).transform.position, out vehicleRoot, out vehicleView))
			{
				RegisterShip(vehicleView, vehicleRoot);
				return true;
			}
			Collider[] array = Physics.OverlapSphere(((Component)player).transform.position, 8f);
			foreach (Collider val in array)
			{
				if (!((Object)(object)val == (Object)null) && TryFindBestVehicleRootFromTransform(((Component)val).transform, ((Component)player).transform.position, out var vehicleRoot2, out var vehicleView2))
				{
					vehicleRoot = vehicleRoot2;
					vehicleView = vehicleView2;
					RegisterShip(vehicleView, vehicleRoot);
					return true;
				}
			}
			return false;
		}

		public static bool IsShipStable(Transform vehicleRoot)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)vehicleRoot == (Object)null)
			{
				return false;
			}
			Rigidbody val = ((Component)vehicleRoot).GetComponent<Rigidbody>() ?? ((Component)vehicleRoot).GetComponentInParent<Rigidbody>();
			if ((Object)(object)val != (Object)null && Mathf.Abs(val.linearVelocity.y) > 0.15f)
			{
				return false;
			}
			return true;
		}

		public static string GetOrCreateShipGuid(ZNetView vehicleView)
		{
			if ((Object)(object)vehicleView == (Object)null || !vehicleView.IsValid())
			{
				return null;
			}
			if (LooksLikeHelperTransform(((Component)vehicleView).transform) && !HasDurableVehicleMarker(((Component)vehicleView).transform, includeChildren: true))
			{
				ValheimRaftBedFixPlugin.DebugLog("Refusing to create ship GUID on helper/transient vehicle object.");
				return null;
			}
			ZDO zDO = vehicleView.GetZDO();
			if (zDO == null)
			{
				return null;
			}
			string text = zDO.GetString("ValheimRaftBedFix_ShipGuid", "");
			if (string.IsNullOrWhiteSpace(text))
			{
				text = Guid.NewGuid().ToString("N");
				zDO.Set("ValheimRaftBedFix_ShipGuid", text);
				ValheimRaftBedFixPlugin.DebugLog("Generated ship GUID " + text);
			}
			return text;
		}

		public static bool TryResolveShipByGuid(string shipGuid, out Transform vehicleRoot, out ZNetView vehicleView)
		{
			vehicleRoot = null;
			vehicleView = null;
			if (string.IsNullOrWhiteSpace(shipGuid))
			{
				return false;
			}
			ZNetView[] array = Object.FindObjectsByType<ZNetView>((FindObjectsSortMode)0);
			foreach (ZNetView val in array)
			{
				if ((Object)(object)val == (Object)null || !val.IsValid())
				{
					continue;
				}
				ZDO zDO = val.GetZDO();
				if (zDO == null || !string.Equals(zDO.GetString("ValheimRaftBedFix_ShipGuid", ""), shipGuid, StringComparison.OrdinalIgnoreCase))
				{
					continue;
				}
				if (!TryNormalizeVehicleRootFromView(val, out var vehicleRoot2, out var vehicleView2))
				{
					ValheimRaftBedFixPlugin.DebugLog("Found GUID " + shipGuid + ", but object is not a durable ship root yet.");
					continue;
				}
				if ((Object)(object)vehicleView2 != (Object)(object)val && (Object)(object)vehicleView2 != (Object)null && vehicleView2.IsValid())
				{
					ZDO zDO2 = vehicleView2.GetZDO();
					if (zDO2 != null && string.IsNullOrWhiteSpace(zDO2.GetString("ValheimRaftBedFix_ShipGuid", "")))
					{
						zDO2.Set("ValheimRaftBedFix_ShipGuid", shipGuid);
					}
				}
				vehicleRoot = vehicleRoot2;
				vehicleView = vehicleView2;
				RegisterShip(vehicleView, vehicleRoot);
				return true;
			}
			return false;
		}

		public static void RegisterShip(ZNetView vehicleView, Transform vehicleRoot)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)vehicleView == (Object)null || !vehicleView.IsValid())
			{
				return;
			}
			ZDO zDO = vehicleView.GetZDO();
			if (zDO != null)
			{
				string @string = zDO.GetString("ValheimRaftBedFix_ShipGuid", "");
				if (!string.IsNullOrWhiteSpace(@string))
				{
					Vector3 val = (((Object)(object)vehicleRoot != (Object)null) ? vehicleRoot.position : ((Component)vehicleView).transform.position);
					Vector2i zone = GetZone(val);
					long num = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
					zDO.Set("ValheimRaftBedFix_ShipRegistryPosition", FormatVector3(val));
					zDO.Set("ValheimRaftBedFix_ShipRegistryZone", FormatZone(zone));
					zDO.Set("ValheimRaftBedFix_ShipRegistryUpdated", num.ToString(CultureInfo.InvariantCulture));
					MobileShipRegistry.RegisterObservedShip(@string, val, zone);
				}
			}
		}

		public static bool TryGetRegisteredShipPosition(string shipGuid, out Vector3 position, out Vector2i zone)
		{
			//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_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: 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)
			position = Vector3.zero;
			zone = new Vector2i(0, 0);
			if (string.IsNullOrWhiteSpace(shipGuid))
			{
				return false;
			}
			if (MobileShipRegistry.TryGetRecord(shipGuid, out var record))
			{
				position = record.Position;
				zone = record.Zone;
				return true;
			}
			ZNetView[] array = Object.FindObjectsByType<ZNetView>((FindObjectsSortMode)0);
			foreach (ZNetView val in array)
			{
				if ((Object)(object)val == (Object)null || !val.IsValid())
				{
					continue;
				}
				ZDO zDO = val.GetZDO();
				if (zDO != null && string.Equals(zDO.GetString("ValheimRaftBedFix_ShipGuid", ""), shipGuid, StringComparison.OrdinalIgnoreCase))
				{
					string @string = zDO.GetString("ValheimRaftBedFix_ShipRegistryPosition", "");
					string string2 = zDO.GetString("ValheimRaftBedFix_ShipRegistryZone", "");
					if (!TryParseVector3(@string, out position))
					{
						position = ((Component)val).transform.position;
					}
					if (!TryParseZone(string2, out zone))
					{
						zone = GetZone(position);
					}
					return true;
				}
			}
			return false;
		}

		public static void ScanLoadedShipsForRegistry()
		{
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return;
			}
			ZNetView[] array = Object.FindObjectsByType<ZNetView>((FindObjectsSortMode)0);
			foreach (ZNetView val in array)
			{
				if ((Object)(object)val == (Object)null || !val.IsValid())
				{
					continue;
				}
				ZDO zDO = val.GetZDO();
				if (zDO == null || !TryNormalizeVehicleRootFromView(val, out var vehicleRoot, out var vehicleView) || (Object)(object)vehicleView == (Object)null || !vehicleView.IsValid())
				{
					continue;
				}
				ZDO zDO2 = vehicleView.GetZDO();
				if (zDO2 != null)
				{
					string @string = zDO.GetString("ValheimRaftBedFix_ShipGuid", "");
					if (!string.IsNullOrWhiteSpace(@string) && string.IsNullOrWhiteSpace(zDO2.GetString("ValheimRaftBedFix_ShipGuid", "")))
					{
						zDO2.Set("ValheimRaftBedFix_ShipGuid", @string);
					}
					if (!string.IsNullOrWhiteSpace(GetOrCreateShipGuid(vehicleView)))
					{
						RegisterShip(vehicleView, vehicleRoot);
					}
				}
			}
		}

		private static bool TryFindBestVehicleRootFromTransform(Transform start, Vector3 referencePosition, out Transform vehicleRoot, out ZNetView vehicleView)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			vehicleRoot = null;
			vehicleView = null;
			if ((Object)(object)start == (Object)null)
			{
				return false;
			}
			int bestScore = int.MinValue;
			Transform bestRoot = null;
			ZNetView bestView = null;
			Transform val = start;
			while ((Object)(object)val != (Object)null)
			{
				ZNetView[] components = ((Component)val).GetComponents<ZNetView>();
				for (int i = 0; i < components.Length; i++)
				{
					ConsiderView(components[i]);
				}
				components = ((Component)val).GetComponentsInParent<ZNetView>(true);
				for (int i = 0; i < components.Length; i++)
				{
					ConsiderView(components[i]);
				}
				if (HasVehicleHintMarker(val, includeChildren: false) || HasDurableVehicleMarker(val, includeChildren: false) || LooksLikeVehicleTransform(val))
				{
					components = ((Component)val).GetComponentsInChildren<ZNetView>(true);
					for (int i = 0; i < components.Length; i++)
					{
						ConsiderView(components[i]);
					}
				}
				val = val.parent;
			}
			if ((Object)(object)bestRoot == (Object)null || (Object)(object)bestView == (Object)null || bestScore < 100)
			{
				return false;
			}
			vehicleRoot = bestRoot;
			vehicleView = bestView;
			return true;
			void ConsiderView(ZNetView view)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				if (TryBuildVehicleCandidate(view, referencePosition, out var candidateRoot, out var candidateView, out var score) && score > bestScore)
				{
					bestScore = score;
					bestRoot = candidateRoot;
					bestView = candidateView;
				}
			}
		}

		private static bool TryNormalizeVehicleRootFromView(ZNetView view, out Transform vehicleRoot, out ZNetView vehicleView)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			vehicleRoot = null;
			vehicleView = null;
			if (!TryBuildVehicleCandidate(view, ((Object)(object)view != (Object)null) ? ((Component)view).transform.position : Vector3.zero, out var candidateRoot, out var candidateView, out var score))
			{
				return false;
			}
			if (score < 100)
			{
				return false;
			}
			vehicleRoot = candidateRoot;
			vehicleView = candidateView;
			return true;
		}

		private static bool TryBuildVehicleCandidate(ZNetView view, Vector3 referencePosition, out Transform candidateRoot, out ZNetView candidateView, out int score)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			candidateRoot = null;
			candidateView = null;
			score = int.MinValue;
			if ((Object)(object)view == (Object)null || !view.IsValid())
			{
				return false;
			}
			if (view.GetZDO() == null)
			{
				return false;
			}
			Transform val = FindBestDurableRoot(((Component)view).transform);
			int num = ScoreVehicleCandidate(val, view, referencePosition);
			if (num < 100)
			{
				return false;
			}
			ZNetView component = ((Component)val).GetComponent<ZNetView>();
			if ((Object)(object)component != (Object)null && component.IsValid() && component.GetZDO() != null)
			{
				candidateView = component;
			}
			else
			{
				candidateView = view;
			}
			candidateRoot = val;
			score = num;
			return true;
		}

		private static Transform FindBestDurableRoot(Transform start)
		{
			if ((Object)(object)start == (Object)null)
			{
				return null;
			}
			Transform result = start;
			int num = ScoreRootTransform(start);
			Transform parent = start.parent;
			while ((Object)(object)parent != (Object)null)
			{
				int num2 = ScoreRootTransform(parent);
				if (num2 > num)
				{
					result = parent;
					num = num2;
				}
				parent = parent.parent;
			}
			return result;
		}

		private static int ScoreVehicleCandidate(Transform root, ZNetView view, Vector3 referencePosition)
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)root == (Object)null || (Object)(object)view == (Object)null)
			{
				return int.MinValue;
			}
			int num = ScoreRootTransform(root);
			if (TryGetValidView(root, out var view2) && (Object)(object)view2 == (Object)(object)view)
			{
				num += 75;
			}
			if ((Object)(object)((Component)root).GetComponent<Rigidbody>() != (Object)null)
			{
				num += 60;
			}
			else if ((Object)(object)((Component)root).GetComponentInParent<Rigidbody>() != (Object)null)
			{
				num += 25;
			}
			Vector3 val = root.position - referencePosition;
			if (((Vector3)(ref val)).sqrMagnitude <= 64f)
			{
				num += 40;
			}
			if (LooksLikeHelperTransform(((Component)view).transform) && (Object)(object)((Component)view).transform == (Object)(object)root)
			{
				num -= 500;
			}
			return num;
		}

		private static int ScoreRootTransform(Transform root)
		{
			if ((Object)(object)root == (Object)null)
			{
				return int.MinValue;
			}
			int num = 0;
			if ((Object)(object)((Component)root).GetComponent<Ship>() != (Object)null)
			{
				num += 1000;
			}
			if (HasDurableVehicleMarker(root, includeChildren: false))
			{
				num += 500;
			}
			if (HasDurableVehicleMarker(root, includeChildren: true))
			{
				num += 250;
			}
			if (HasWeakVehicleMarker(root, includeChildren: false))
			{
				num += 100;
			}
			if (HasWeakVehicleMarker(root, includeChildren: true))
			{
				num += 50;
			}
			if (HasOnlyHelperMarker(root))
			{
				num -= 300;
			}
			if (LooksLikeVehicleTransform(root))
			{
				num += 50;
			}
			if (LooksLikeHelperTransform(root))
			{
				num -= 400;
			}
			return num;
		}

		private static bool TryGetValidView(Transform transform, out ZNetView view)
		{
			view = null;
			if ((Object)(object)transform == (Object)null)
			{
				return false;
			}
			view = ((Component)transform).GetComponent<ZNetView>() ?? ((Component)transform).GetComponentInParent<ZNetView>() ?? ((Component)transform).GetComponentInChildren<ZNetView>();
			if ((Object)(object)view != (Object)null && view.IsValid())
			{
				return view.GetZDO() != null;
			}
			return false;
		}

		private static bool HasDurableVehicleMarker(Transform transform, bool includeChildren)
		{
			return HasComponentName(transform, includeChildren, IsDurableVehicleComponentName);
		}

		private static bool HasWeakVehicleMarker(Transform transform, bool includeChildren)
		{
			return HasComponentName(transform, includeChildren, IsWeakVehicleComponentName);
		}

		private static bool HasVehicleHintMarker(Transform transform, bool includeChildren)
		{
			return HasComponentName(transform, includeChildren, IsVehicleHintComponentName);
		}

		private static bool HasOnlyHelperMarker(Transform transform)
		{
			if ((Object)(object)transform == (Object)null)
			{
				return false;
			}
			bool num = HasVehicleHintMarker(transform, includeChildren: false);
			bool flag = HasDurableVehicleMarker(transform, includeChildren: false);
			if (num)
			{
				return !flag;
			}
			return false;
		}

		private static bool HasComponentName(Transform transform, bool includeChildren, Func<string, bool> predicate)
		{
			if ((Object)(object)transform == (Object)null)
			{
				return false;
			}
			Component[] array = (includeChildren ? ((Component)transform).GetComponentsInChildren<Component>(true) : ((Component)transform).GetComponents<Component>());
			foreach (Component val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					string text = ((object)val).GetType().FullName ?? ((object)val).GetType().Name ?? string.Empty;
					if (predicate(text.ToLowerInvariant()))
					{
						return true;
					}
				}
			}
			return false;
		}

		private static bool IsDurableVehicleComponentName(string typeName)
		{
			if (string.IsNullOrWhiteSpace(typeName))
			{
				return false;
			}
			if (!typeName.Contains("vehiclemovementcontroller") && !typeName.Contains("vehiclepiecescontroller") && !typeName.Contains("watervehicle") && !typeName.Contains("moveablebaserootcomponent"))
			{
				return typeName.Contains("moveablebaseshipcomponent");
			}
			return true;
		}

		private static bool IsWeakVehicleComponentName(string typeName)
		{
			if (string.IsNullOrWhiteSpace(typeName))
			{
				return false;
			}
			if (!typeName.Contains("vehicleonboardcontroller") && !typeName.Contains("valheimraft"))
			{
				return typeName.Contains("valheimvehicles");
			}
			return true;
		}

		private static bool IsVehicleHintComponentName(string typeName)
		{
			if (string.IsNullOrWhiteSpace(typeName))
			{
				return false;
			}
			if (!IsDurableVehicleComponentName(typeName) && !IsWeakVehicleComponentName(typeName))
			{
				return typeName.Contains("convexhullboundaryconstraint");
			}
			return true;
		}

		private static bool LooksLikeVehicleTransform(Transform transform)
		{
			if ((Object)(object)transform == (Object)null)
			{
				return false;
			}
			string text = ((Object)transform).name.ToLowerInvariant();
			if (!text.Contains("raft") && !text.Contains("ship") && !text.Contains("vehicle"))
			{
				return text.Contains("moveablebase");
			}
			return true;
		}

		private static bool LooksLikeHelperTransform(Transform transform)
		{
			if ((Object)(object)transform == (Object)null)
			{
				return false;
			}
			string text = ((Object)transform).name.ToLowerInvariant();
			if (!text.Contains("convexhull") && !text.Contains("convex_hull") && !text.Contains("boundary") && !text.Contains("constraint") && !text.Contains("collider") && !text.Contains("trigger") && !text.Contains("helper"))
			{
				return text.Contains("valheimvehicles_convexhull");
			}
			return true;
		}

		private static Vector2i GetZone(Vector3 position)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return MobileZoneMath.GetZone(position);
		}

		private static string FormatVector3(Vector3 value)
		{
			return value.x.ToString(CultureInfo.InvariantCulture) + "," + value.y.ToString(CultureInfo.InvariantCulture) + "," + value.z.ToString(CultureInfo.InvariantCulture);
		}

		private static bool TryParseVector3(string raw, out Vector3 value)
		{
			//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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			value = Vector3.zero;
			if (string.IsNullOrWhiteSpace(raw))
			{
				return false;
			}
			string[] array = raw.Split(',');
			if (array.Length != 3)
			{
				return false;
			}
			if (!float.TryParse(array[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return false;
			}
			if (!float.TryParse(array[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
			{
				return false;
			}
			if (!float.TryParse(array[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3))
			{
				return false;
			}
			value = new Vector3(result, result2, result3);
			return true;
		}

		private static string FormatZone(Vector2i zone)
		{
			return $"{zone.x},{zone.y}";
		}

		private static bool TryParseZone(string raw, out Vector2i zone)
		{
			zone = new Vector2i(0, 0);
			if (string.IsNullOrWhiteSpace(raw))
			{
				return false;
			}
			string[] array = raw.Split(',');
			if (array.Length != 2)
			{
				return false;
			}
			if (!int.TryParse(array[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				return false;
			}
			if (!int.TryParse(array[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2))
			{
				return false;
			}
			zone = new Vector2i(result, result2);
			return true;
		}
	}
	public static class MobileShipRegistry
	{
		public sealed class ShipRecord
		{
			public string ShipGuid;

			public Vector3 Position;

			public Vector2i Zone;

			public long UpdatedUnixSeconds;
		}

		private const string RpcRegisterShip = "ValheimRaftBedFix_RPC_RegisterShip";

		private const string RpcRequestShip = "ValheimRaftBedFix_RPC_RequestShip";

		private const string RpcShipRecord = "ValheimRaftBedFix_RPC_ShipRecord";

		private const float ClientRegisterThrottleSeconds = 1f;

		private const float ClientRequestThrottleSeconds = 1f;

		private const float SaveThrottleSeconds = 10f;

		private static readonly Dictionary<string, ShipRecord> Records = new Dictionary<string, ShipRecord>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, float> NextClientRegisterTime = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, float> NextClientRequestTime = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);

		private static ZRoutedRpc _registeredRpcInstance;

		private static long _loadedWorldId = long.MinValue;

		private static bool _serverRegistryLoaded;

		private static bool _serverRegistryDirty;

		private static float _nextServerRegistrySaveTime;

		private static bool IsServer
		{
			get
			{
				if ((Object)(object)ZNet.instance != (Object)null)
				{
					return ZNet.instance.IsServer();
				}
				return false;
			}
		}

		private static long CurrentWorldId
		{
			get
			{
				if (!((Object)(object)ZNet.instance != (Object)null))
				{
					return 0L;
				}
				return ZNet.instance.GetWorldUID();
			}
		}

		private static string RegistryPath => Path.Combine(Paths.ConfigPath, $"ValheimRaftBedFix.ship-registry.server.{CurrentWorldId}.txt");

		public static void Update()
		{
			RegisterRpcsIfNeeded();
			ResetForWorldIfNeeded();
			if (IsServer)
			{
				LoadServerRegistryIfNeeded();
				if (_serverRegistryDirty && Time.realtimeSinceStartup >= _nextServerRegistrySaveTime)
				{
					SaveServerRegistry();
				}
			}
		}

		public static void FlushServerRegistryIfDirty()
		{
			if (IsServer)
			{
				LoadServerRegistryIfNeeded();
				SaveServerRegistry();
			}
		}

		public static void RegisterObservedShip(string shipGuid, Vector3 position, Vector2i zone)
		{
			//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)
			if (!string.IsNullOrWhiteSpace(shipGuid))
			{
				long updatedUnixSeconds = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
				ShipRecord record = new ShipRecord
				{
					ShipGuid = shipGuid,
					Position = position,
					Zone = zone,
					UpdatedUnixSeconds = updatedUnixSeconds
				};
				UpsertClientCache(record);
				if (IsServer)
				{
					LoadServerRegistryIfNeeded();
					UpsertServerRecord(record, saveSoon: false);
				}
				else
				{
					SendObservedShipToServer(record);
				}
			}
		}

		public static bool TryGetRecord(string shipGuid, out ShipRecord record)
		{
			record = null;
			if (string.IsNullOrWhiteSpace(shipGuid))
			{
				return false;
			}
			ResetForWorldIfNeeded();
			if (IsServer)
			{
				LoadServerRegistryIfNeeded();
			}
			if (!Records.TryGetValue(shipGuid, out var value))
			{
				return false;
			}
			record = CloneRecord(value);
			return true;
		}

		public static void RequestShipRecord(string shipGuid)
		{
			if (string.IsNullOrWhiteSpace(shipGuid))
			{
				return;
			}
			RegisterRpcsIfNeeded();
			ResetForWorldIfNeeded();
			if (IsServer)
			{
				LoadServerRegistryIfNeeded();
			}
			else if (ZRoutedRpc.instance != null)
			{
				float realtimeSinceStartup = Time.realtimeSinceStartup;
				if (!NextClientRequestTime.TryGetValue(shipGuid, out var value) || !(realtimeSinceStartup < value))
				{
					NextClientRequestTime[shipGuid] = realtimeSinceStartup + 1f;
					ZRoutedRpc.instance.InvokeRoutedRPC(GetServerPeerId(), "ValheimRaftBedFix_RPC_RequestShip", new object[1] { shipGuid });
				}
			}
		}

		private static void SendObservedShipToServer(ShipRecord record)
		{
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			if (record == null || ZRoutedRpc.instance == null)
			{
				return;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (NextClientRegisterTime.TryGetValue(record.ShipGuid, out var value) && realtimeSinceStartup < value && Records.TryGetValue(record.ShipGuid, out var value2))
			{
				Vector3 val = value2.Position - record.Position;
				if (((Vector3)(ref val)).sqrMagnitude < 4f)
				{
					return;
				}
			}
			NextClientRegisterTime[record.ShipGuid] = realtimeSinceStartup + 1f;
			ZRoutedRpc.instance.InvokeRoutedRPC(GetServerPeerId(), "ValheimRaftBedFix_RPC_RegisterShip", new object[3]
			{
				record.ShipGuid ?? string.Empty,
				record.Position,
				FormatZone(record.Zone)
			});
		}

		private static void RegisterRpcsIfNeeded()
		{
			if (ZRoutedRpc.instance != null && _registeredRpcInstance != ZRoutedRpc.instance)
			{
				_registeredRpcInstance = ZRoutedRpc.instance;
				ZRoutedRpc.instance.Register<string, Vector3, string>("ValheimRaftBedFix_RPC_RegisterShip", (Action<long, string, Vector3, string>)RPC_RegisterShip);
				ZRoutedRpc.instance.Register<string>("ValheimRaftBedFix_RPC_RequestShip", (Action<long, string>)RPC_RequestShip);
				ZRoutedRpc.instance.Register<string, bool, Vector3, string>("ValheimRaftBedFix_RPC_ShipRecord", (Method<string, bool, Vector3, string>)RPC_ShipRecord);
			}
		}

		private static void RPC_RegisterShip(long sender, string shipGuid, Vector3 position, string zoneRaw)
		{
			//IL_001d: 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_0035: Unknown result type (might be due to invalid IL or missing references)
			if (!IsServer)
			{
				return;
			}
			try
			{
				if (!string.IsNullOrWhiteSpace(shipGuid))
				{
					Vector2i zone;
					Vector2i zone2 = (TryParseZone(zoneRaw, out zone) ? zone : GetZone(position));
					ShipRecord record = new ShipRecord
					{
						ShipGuid = shipGuid,
						Position = position,
						Zone = zone2,
						UpdatedUnixSeconds = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
					};
					LoadServerRegistryIfNeeded();
					UpsertServerRecord(record, saveSoon: false);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = ValheimRaftBedFixPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Failed to handle ship registry update RPC: " + ex.Message));
				}
			}
		}

		private static void RPC_RequestShip(long sender, string shipGuid)
		{
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			if (!IsServer)
			{
				return;
			}
			try
			{
				if (!string.IsNullOrWhiteSpace(shipGuid))
				{
					LoadServerRegistryIfNeeded();
					if (Records.TryGetValue(shipGuid, out var value))
					{
						ZRoutedRpc.instance.InvokeRoutedRPC(sender, "ValheimRaftBedFix_RPC_ShipRecord", new object[4]
						{
							shipGuid,
							true,
							value.Position,
							FormatZone(value.Zone)
						});
					}
					else
					{
						ZRoutedRpc.instance.InvokeRoutedRPC(sender, "ValheimRaftBedFix_RPC_ShipRecord", new object[4]
						{
							shipGuid,
							false,
							Vector3.zero,
							string.Empty
						});
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = ValheimRaftBedFixPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Failed to handle ship registry request RPC: " + ex.Message));
				}
			}
		}

		private static void RPC_ShipRecord(long sender, string shipGuid, bool found, Vector3 position, string zoneRaw)
		{
			//IL_001b: 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_0033: 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)
			try
			{
				if (found && !string.IsNullOrWhiteSpace(shipGuid))
				{
					Vector2i zone;
					Vector2i zone2 = (TryParseZone(zoneRaw, out zone) ? zone : GetZone(position));
					ShipRecord shipRecord = new ShipRecord
					{
						ShipGuid = shipGuid,
						Position = position,
						Zone = zone2,
						UpdatedUnixSeconds = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
					};
					UpsertClientCache(shipRecord);
					ValheimRaftBedFixPlugin.DebugLog($"Received server ship registry record {shipRecord.ShipGuid} at {shipRecord.Position} zone {shipRecord.Zone.x},{shipRecord.Zone.y}");
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = ValheimRaftBedFixPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Failed to handle ship registry response RPC: " + ex.Message));
				}
			}
		}

		private static void UpsertClientCache(ShipRecord record)
		{
			if (record != null && !string.IsNullOrWhiteSpace(record.ShipGuid))
			{
				ResetForWorldIfNeeded();
				if (!Records.TryGetValue(record.ShipGuid, out var value) || value.UpdatedUnixSeconds <= record.UpdatedUnixSeconds)
				{
					Records[record.ShipGuid] = CloneRecord(record);
				}
			}
		}

		private static void UpsertServerRecord(ShipRecord record, bool saveSoon)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			if (!IsServer || record == null || string.IsNullOrWhiteSpace(record.ShipGuid))
			{
				return;
			}
			LoadServerRegistryIfNeeded();
			bool flag = false;
			if (!Records.TryGetValue(record.ShipGuid, out var value))
			{
				value = new ShipRecord
				{
					ShipGuid = record.ShipGuid
				};
				Records[record.ShipGuid] = value;
				flag = true;
			}
			else
			{
				Vector3 val = value.Position - record.Position;
				if (((Vector3)(ref val)).sqrMagnitude > 1f || value.Zone.x != record.Zone.x || value.Zone.y != record.Zone.y || value.UpdatedUnixSeconds <= 0)
				{
					flag = true;
				}
			}
			if (flag)
			{
				value.Position = record.Position;
				value.Zone = record.Zone;
				value.UpdatedUnixSeconds = Math.Max(record.UpdatedUnixSeconds, value.UpdatedUnixSeconds);
				_serverRegistryDirty = true;
				_nextServerRegistrySaveTime = (saveSoon ? Time.realtimeSinceStartup : (Time.realtimeSinceStartup + 10f));
				ValheimRaftBedFixPlugin.DebugLog($"Server ship registry updated {record.ShipGuid} at {record.Position} zone {record.Zone.x},{record.Zone.y}");
			}
		}

		private static ShipRecord CloneRecord(ShipRecord source)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			if (source == null)
			{
				return null;
			}
			return new ShipRecord
			{
				ShipGuid = source.ShipGuid,
				Position = source.Position,
				Zone = source.Zone,
				UpdatedUnixSeconds = source.UpdatedUnixSeconds
			};
		}

		private static void ResetForWorldIfNeeded()
		{
			long currentWorldId = CurrentWorldId;
			if (_loadedWorldId != currentWorldId)
			{
				Records.Clear();
				NextClientRegisterTime.Clear();
				NextClientRequestTime.Clear();
				_loadedWorldId = currentWorldId;
				_serverRegistryLoaded = false;
				_serverRegistryDirty = false;
				_nextServerRegistrySaveTime = 0f;
			}
		}

		private static void LoadServerRegistryIfNeeded()
		{
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			if (!IsServer)
			{
				return;
			}
			ResetForWorldIfNeeded();
			if (_serverRegistryLoaded)
			{
				return;
			}
			_serverRegistryLoaded = true;
			_serverRegistryDirty = false;
			string registryPath = RegistryPath;
			if (!File.Exists(registryPath))
			{
				return;
			}
			try
			{
				string[] array = File.ReadAllLines(registryPath);
				foreach (string text in array)
				{
					if (string.IsNullOrWhiteSpace(text))
					{
						continue;
					}
					string[] array2 = text.Split('|');
					if (array2.Length != 4)
					{
						continue;
					}
					string text2 = array2[0];
					if (!string.IsNullOrWhiteSpace(text2) && TryParseVector3(array2[1], out var value))
					{
						if (!TryParseZone(array2[2], out var zone))
						{
							zone = GetZone(value);
						}
						if (!long.TryParse(array2[3], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
						{
							result = 0L;
						}
						Records[text2] = new ShipRecord
						{
							ShipGuid = text2,
							Position = value,
							Zone = zone,
							UpdatedUnixSeconds = result
						};
					}
				}
				ValheimRaftBedFixPlugin.DebugLog($"Loaded {Records.Count} server ship registry records.");
			}
			catch (Exception ex)
			{
				ManualLogSource log = ValheimRaftBedFixPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Failed to load server ship registry: " + ex.Message));
				}
			}
		}

		private static void SaveServerRegistry()
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			if (!IsServer || !_serverRegistryDirty)
			{
				return;
			}
			try
			{
				string registryPath = RegistryPath;
				string directoryName = Path.GetDirectoryName(registryPath);
				if (!string.IsNullOrEmpty(directoryName))
				{
					Directory.CreateDirectory(directoryName);
				}
				List<string> list = new List<string>();
				foreach (ShipRecord value in Records.Values)
				{
					if (value != null && !string.IsNullOrWhiteSpace(value.ShipGuid))
					{
						list.Add(value.ShipGuid + "|" + FormatVector3(value.Position) + "|" + FormatZone(value.Zone) + "|" + value.UpdatedUnixSeconds.ToString(CultureInfo.InvariantCulture));
					}
				}
				File.WriteAllLines(registryPath, list);
				_serverRegistryDirty = false;
				_nextServerRegistrySaveTime = 0f;
			}
			catch (Exception ex)
			{
				ManualLogSource log = ValheimRaftBedFixPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Failed to save server ship registry: " + ex.Message));
				}
			}
		}

		private static long GetServerPeerId()
		{
			try
			{
				long? num = TryInvokeLongMethod(typeof(ZRoutedRpc), ZRoutedRpc.instance, "GetServerPeerID");
				if (num.HasValue)
				{
					return num.Value;
				}
				num = TryInvokeLongMethod(typeof(ZRoutedRpc), ZRoutedRpc.instance, "GetServerPeerId");
				if (num.HasValue)
				{
					return num.Value;
				}
				long? num2 = TryGetLongMember(typeof(ZNet), ZNet.instance, "m_serverPeerID");
				if (num2.HasValue)
				{
					return num2.Value;
				}
				num2 = TryGetLongMember(typeof(ZNet), ZNet.instance, "m_serverPeerId");
				if (num2.HasValue)
				{
					return num2.Value;
				}
			}
			catch (Exception ex)
			{
				ValheimRaftBedFixPlugin.DebugLog("Could not determine server peer id reflectively: " + ex.Message);
			}
			return 0L;
		}

		private static long? TryInvokeLongMethod(Type type, object instance, string methodName)
		{
			if (type == null || string.IsNullOrWhiteSpace(methodName))
			{
				return null;
			}
			MethodInfo method = type.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
			if (method == null)
			{
				return null;
			}
			object obj = (method.IsStatic ? null : instance);
			if (!method.IsStatic && obj == null)
			{
				return null;
			}
			return ConvertToLong(method.Invoke(obj, null));
		}

		private static long? TryGetLongMember(Type type, object instance, string memberName)
		{
			if (type == null || instance == null || string.IsNullOrWhiteSpace(memberName))
			{
				return null;
			}
			FieldInfo field = type.GetField(memberName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				object obj = (field.IsStatic ? null : instance);
				return ConvertToLong(field.GetValue(obj));
			}
			PropertyInfo property = type.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null)
			{
				MethodInfo getMethod = property.GetGetMethod(nonPublic: true);
				if (getMethod == null)
				{
					return null;
				}
				object obj2 = (getMethod.IsStatic ? null : instance);
				return ConvertToLong(property.GetValue(obj2, null));
			}
			return null;
		}

		private static long? ConvertToLong(object value)
		{
			if (value == null)
			{
				return null;
			}
			if (value is long)
			{
				return (long)value;
			}
			if (value is int num)
			{
				return num;
			}
			if (value is uint num2)
			{
				return num2;
			}
			if (value is ulong num3 && num3 <= long.MaxValue)
			{
				return (long)num3;
			}
			return null;
		}

		private static Vector2i GetZone(Vector3 position)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return MobileZoneMath.GetZone(position);
		}

		private static string FormatVector3(Vector3 value)
		{
			return value.x.ToString(CultureInfo.InvariantCulture) + "," + value.y.ToString(CultureInfo.InvariantCulture) + "," + value.z.ToString(CultureInfo.InvariantCulture);
		}

		private static bool TryParseVector3(string raw, out Vector3 value)
		{
			//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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			value = Vector3.zero;
			if (string.IsNullOrWhiteSpace(raw))
			{
				return false;
			}
			string[] array = raw.Split(',');
			if (array.Length != 3)
			{
				return false;
			}
			if (!float.TryParse(array[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return false;
			}
			if (!float.TryParse(array[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
			{
				return false;
			}
			if (!float.TryParse(array[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3))
			{
				return false;
			}
			value = new Vector3(result, result2, result3);
			return true;
		}

		private static string FormatZone(Vector2i zone)
		{
			return $"{zone.x},{zone.y}";
		}

		private static bool TryParseZone(string raw, out Vector2i zone)
		{
			zone = new Vector2i(0, 0);
			if (string.IsNullOrWhiteSpace(raw))
			{
				return false;
			}
			string[] array = raw.Split(',');
			if (array.Length != 2)
			{
				return false;
			}
			if (!int.TryParse(array[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				return false;
			}
			if (!int.TryParse(array[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2))
			{
				return false;
			}
			zone = new Vector2i(result, result2);
			return true;
		}
	}
	public static class MobileSpawnData
	{
		private const string Prefix = "ValheimRaftBedFix_MobileSpawn";

		public const string BedGuidZdoKey = "ValheimRaftBedFix_BedGuid";

		public const string ShipGuidZdoKey = "ValheimRaftBedFix_ShipGuid";

		private static long WorldId
		{
			get
			{
				if (!((Object)(object)ZNet.instance != (Object)null))
				{
					return 0L;
				}
				return ZNet.instance.GetWorldUID();
			}
		}

		public static string BedGuidPlayerKey => string.Format("{0}_BedGuid_{1}", "ValheimRaftBedFix_MobileSpawn", WorldId);

		public static string ShipGuidPlayerKey => string.Format("{0}_ShipGuid_{1}", "ValheimRaftBedFix_MobileSpawn", WorldId);

		public static string LogoutKey => string.Format("{0}_IsMobileLogout_{1}", "ValheimRaftBedFix_MobileSpawn", WorldId);

		public static string OffsetKey => string.Format("{0}_LocalOffset_{1}", "ValheimRaftBedFix_MobileSpawn", WorldId);

		public static string ShipWorldPositionKey => string.Format("{0}_ShipWorldPosition_{1}", "ValheimRaftBedFix_MobileSpawn", WorldId);

		public static string ShipZoneKey => string.Format("{0}_ShipZone_{1}", "ValheimRaftBedFix_MobileSpawn", WorldId);

		public static bool HasAnyStoredMobileData(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			if (TryGetMobileLogout(player, out var shipGuid, out var _))
			{
				return true;
			}
			return TryGetBedGuid(player, out shipGuid);
		}

		public static void SetBed(Player player, string bedGuid)
		{
			if (!((Object)(object)player == (Object)null) && !string.IsNullOrWhiteSpace(bedGuid))
			{
				player.m_customData[BedGuidPlayerKey] = bedGuid;
			}
		}

		public static bool TryGetBedGuid(Player player, out string bedGuid)
		{
			bedGuid = null;
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			if (!player.m_customData.TryGetValue(BedGuidPlayerKey, out var value))
			{
				return false;
			}
			if (string.IsNullOrWhiteSpace(value))
			{
				return false;
			}
			bedGuid = value;
			return true;
		}

		public static void ClearStoredBed(Player player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				player.m_customData.Remove(BedGuidPlayerKey);
			}
		}

		public static void ClearStoredBedIfMatches(Player player, string bedGuid)
		{
			if (!((Object)(object)player == (Object)null) && !string.IsNullOrWhiteSpace(bedGuid) && TryGetBedGuid(player, out var bedGuid2) && string.Equals(bedGuid2, bedGuid, StringComparison.OrdinalIgnoreCase))
			{
				ClearStoredBed(player);
			}
		}

		public static void ClearLocalStoredBedIfMatches(string bedGuid)
		{
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null))
			{
				ClearStoredBedIfMatches(localPlayer, bedGuid);
			}
		}

		public static void SetMobileLogout(Player player, string shipGuid, Vector3 localOffset)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			Vector3 shipWorldPosition = (((Object)(object)player != (Object)null) ? ((Component)player).transform.position : Vector3.zero);
			SetMobileLogout(player, shipGuid, localOffset, shipWorldPosition);
		}

		public static void SetMobileLogout(Player player, string shipGuid, Vector3 localOffset, Vector3 shipWorldPosition)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)player == (Object)null) && !string.IsNullOrWhiteSpace(shipGuid))
			{
				player.m_customData[LogoutKey] = "true";
				player.m_customData[ShipGuidPlayerKey] = shipGuid;
				player.m_customData[OffsetKey] = ToString(localOffset);
				player.m_customData[ShipWorldPositionKey] = ToString(shipWorldPosition);
				Vector2i zone = GetZone(shipWorldPosition);
				player.m_customData[ShipZoneKey] = $"{zone.x},{zone.y}";
			}
		}

		public static void ClearMobileLogout(Player player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				player.m_customData[LogoutKey] = "false";
				player.m_customData.Remove(ShipGuidPlayerKey);
				player.m_customData.Remove(OffsetKey);
				player.m_customData.Remove(ShipWorldPositionKey);
				player.m_customData.Remove(ShipZoneKey);
			}
		}

		public static void ClearAllMobileData(Player player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				ClearMobileLogout(player);
				ClearStoredBed(player);
			}
		}

		public static bool TryGetMobileLogout(Player player, out string shipGuid, out Vector3 localOffset)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			shipGuid = null;
			localOffset = Vector3.zero;
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			if (!player.m_customData.TryGetValue(LogoutKey, out var value))
			{
				return false;
			}
			if (!string.Equals(value, "true", StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			if (!player.m_customData.TryGetValue(ShipGuidPlayerKey, out shipGuid))
			{
				return false;
			}
			if (string.IsNullOrWhiteSpace(shipGuid))
			{
				return false;
			}
			if (!player.m_customData.TryGetValue(OffsetKey, out var value2))
			{
				return false;
			}
			return TryParseVector3(value2, out localOffset);
		}

		public static bool TryGetStoredShipPosition(Player player, out Vector3 position)
		{
			//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)
			position = Vector3.zero;
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			if (!player.m_customData.TryGetValue(ShipWorldPositionKey, out var value))
			{
				return false;
			}
			return TryParseVector3(value, out position);
		}

		public static bool TryGetStoredShipZone(Player player, out Vector2i zone)
		{
			zone = new Vector2i(0, 0);
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			if (!player.m_customData.TryGetValue(ShipZoneKey, out var value))
			{
				return false;
			}
			return TryParseZone(value, out zone);
		}

		public static string GetOrCreateBedGuid(Bed bed)
		{
			if ((Object)(object)bed == (Object)null)
			{
				return null;
			}
			ZNetView component = ((Component)bed).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return null;
			}
			ZDO zDO = component.GetZDO();
			if (zDO == null)
			{
				return null;
			}
			string text = zDO.GetString("ValheimRaftBedFix_BedGuid", "");
			if (string.IsNullOrWhiteSpace(text))
			{
				text = Guid.NewGuid().ToString("N");
				zDO.Set("ValheimRaftBedFix_BedGuid", text);
				ValheimRaftBedFixPlugin.DebugLog("Generated bed GUID " + text);
			}
			return text;
		}

		public static string GetExistingBedGuid(Bed bed)
		{
			if ((Object)(object)bed == (Object)null)
			{
				return null;
			}
			ZNetView component = ((Component)bed).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return null;
			}
			ZDO zDO = component.GetZDO();
			if (zDO == null)
			{
				return null;
			}
			string @string = zDO.GetString("ValheimRaftBedFix_BedGuid", "");
			if (!string.IsNullOrWhiteSpace(@string))
			{
				return @string;
			}
			return null;
		}

		public static bool TryResolveBedByGuid(string bedGuid, out Bed bed, out ZNetView bedView)
		{
			bed = null;
			bedView = null;
			if (string.IsNullOrWhiteSpace(bedGuid))
			{
				return false;
			}
			Bed[] array = Object.FindObjectsByType<Bed>((FindObjectsSortMode)0);
			foreach (Bed val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				ZNetView component = ((Component)val).GetComponent<ZNetView>();
				if (!((Object)(object)component == (Object)null) && component.IsValid())
				{
					ZDO zDO = component.GetZDO();
					if (zDO != null && string.Equals(zDO.GetString("ValheimRaftBedFix_BedGuid", ""), bedGuid, StringComparison.OrdinalIgnoreCase))
					{
						bed = val;
						bedView = component;
						return true;
					}
				}
			}
			return false;
		}

		public static bool TryFindNearbyBed(Player player, float radius, out Bed nearbyBed)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			nearbyBed = null;
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			Vector3 position = ((Component)player).transform.position;
			float num = radius * radius;
			float num2 = float.MaxValue;
			Bed[] array = Object.FindObjectsByType<Bed>((FindObjectsSortMode)0);
			foreach (Bed val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				ZNetView component = ((Component)val).GetComponent<ZNetView>();
				if (!((Object)(object)component == (Object)null) && component.IsValid())
				{
					Vector3 val2 = ((Component)val).transform.position - position;
					float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
					if (!(sqrMagnitude > num) && !(sqrMagnitude >= num2))
					{
						num2 = sqrMagnitude;
						nearbyBed = val;
					}
				}
			}
			return (Object)(object)nearbyBed != (Object)null;
		}

		public static string Vector3ToString(Vector3 value)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return ToString(value);
		}

		public static bool TryParseStoredVector3(string raw, out Vector3 value)
		{
			return TryParseVector3(raw, out value);
		}

		public static string ZoneToString(Vector2i zone)
		{
			return $"{zone.x},{zone.y}";
		}

		public static bool TryParseStoredZone(string raw, out Vector2i zone)
		{
			return TryParseZone(raw, out zone);
		}

		private static Vector2i GetZone(Vector3 position)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return MobileZoneMath.GetZone(position);
		}

		private static string ToString(Vector3 v)
		{
			return v.x.ToString(CultureInfo.InvariantCulture) + "," + v.y.ToString(CultureInfo.InvariantCulture) + "," + v.z.ToString(CultureInfo.InvariantCulture);
		}

		private static bool TryParseVector3(string raw, out Vector3 value)
		{
			//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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			value = Vector3.zero;
			if (string.IsNullOrWhiteSpace(raw))
			{
				return false;
			}
			string[] array = raw.Split(',');
			if (array.Length != 3)
			{
				return false;
			}
			if (!float.TryParse(array[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return false;
			}
			if (!float.TryParse(array[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
			{
				return false;
			}
			if (!float.TryParse(array[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3))
			{
				return false;
			}
			value = new Vector3(result, result2, result3);
			return true;
		}

		private static bool TryParseZone(string raw, out Vector2i zone)
		{
			zone = new Vector2i(0, 0);
			if (string.IsNullOrWhiteSpace(raw))
			{
				return false;
			}
			string[] array = raw.Split(',');
			if (array.Length != 2)
			{
				return false;
			}
			if (!int.TryParse(array[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				return false;
			}
			if (!int.TryParse(array[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2))
			{
				return false;
			}
			zone = new Vector2i(result, result2);
			return true;
		}
	}
	public sealed class MobileSpawnState
	{
		public long BedID;

		public bool IsMobileLogout;

		public Vector3 LocalOffset;
	}
	public readonly struct Vector2i
	{
		public readonly int x;

		public readonly int y;

		public Vector2i(int x, int y)
		{
			this.x = x;
			this.y = y;
		}

		public override string ToString()
		{
			return x.ToString(CultureInfo.InvariantCulture) + "," + y.ToString(CultureInfo.InvariantCulture);
		}
	}
	internal static class MobileZoneMath
	{
		private const float ZoneSize = 64f;

		public static Vector2i GetZone(Vector3 position)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			int x = Mathf.FloorToInt((position.x + 32f) / 64f);
			int y = Mathf.FloorToInt((position.z + 32f) / 64f);
			return new Vector2i(x, y);
		}
	}
	[HarmonyPatch(typeof(WearNTear), "Destroy")]
	internal static class BedDestroyedPatch
	{
		private static void Prefix(WearNTear __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			Bed val = ((Component)__instance).GetComponent<Bed>() ?? ((Component)__instance).GetComponentInParent<Bed>() ?? ((Component)__instance).GetComponentInChildren<Bed>();
			if (!((Object)(object)val == (Object)null))
			{
				string existingBedGuid = MobileSpawnData.GetExistingBedGuid(val);
				if (!string.IsNullOrWhiteSpace(existingBedGuid))
				{
					MobileSpawnData.ClearLocalStoredBedIfMatches(existingBedGuid);
					ValheimRaftBedFixPlugin.DebugLog("Destroyed mobile bed GUID " + existingBedGuid + "; cleared local stored bed reference if present.");
				}
			}
		}
	}
	[BepInPlugin("DarkestReponse.valheimraftbedfix", "ValheimRaftBedFix", "0.1.5")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class ValheimRaftBedFixPlugin : BaseUnityPlugin
	{
		private enum RestoreState
		{
			None,
			Waiting,
			Restoring,
			Success,
			FailedFallback
		}

		public const string ModGUID = "DarkestReponse.valheimraftbedfix";

		public const string ModName = "ValheimRaftBedFix";

		public const string ModVersion = "0.1.5";

		public static ConfigEntry<bool> DebugMode;

		public static ConfigEntry<int> MaxRestoreRetries;

		public static ConfigEntry<float> RetryDelaySeconds;

		public static ConfigEntry<bool> EnableCenterMessages;

		public static ConfigEntry<float> HoldingHeight;

		public static ConfigEntry<float> PostRestoreTrackingBlockSeconds;

		public static ConfigEntry<int> StableFramesRequired;

		public static ConfigEntry<float> ServerRegistryScanInterval;

		public static readonly Vector3 TemporaryHoldingPosition = new Vector3(0f, 10000f, 0f);

		private readonly Harmony _harmony = new Harmony("DarkestReponse.valheimraftbedfix");

		private RestoreState _restoreState;

		private float _nextShipSampleTime;

		private float _nextRestoreAttemptTime;

		private float _nextRestoreMessageTime;

		private float _nextZoneLoadPulseTime;

		private float _nextServerRegistryScanTime;

		private float _trackingBlockedUntil;

		private int _restoreAttempts;

		private int _stableFrames;

		private bool _hasLastRootY;

		private float _lastRootY;

		private string _activeShipGuid;

		private Vector3 _activeLocalOffset;

		private Vector3 _activeHoldPosition;

		private Quaternion _lastSafeRotation = Quaternion.identity;

		private Rigidbody _suspendedBody;

		private bool _savedUseGravity;

		private bool _savedGodMode;

		private bool _savedHadGodModeField;

		private FieldInfo _godModeField;

		public static bool RestoreInProgress { get; set; }

		public static ValheimRaftBedFixPlugin Instance { get; private set; }

		public static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			DebugMode = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugMode", false, "Enable verbose debug logging.");
			MaxRestoreRetries = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaxRestoreRetries", 120, "Maximum number of ship restore retry attempts.");
			RetryDelaySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "RetryDelaySeconds", 0.5f, "Delay in seconds between ship restore attempts.");
			EnableCenterMessages = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableCenterMessages", true, "Show center-screen status messages during ship restoration.");
			HoldingHeight = ((BaseUnityPlugin)this).Config.Bind<float>("General", "HoldingHeight", 10000f, "Y height used while holding a player above the stored ship zone.");
			PostRestoreTrackingBlockSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "PostRestoreTrackingBlockSeconds", 8f, "Seconds to block logout tracking after restore/fallback.");
			StableFramesRequired = ((BaseUnityPlugin)this).Config.Bind<int>("General", "StableFramesRequired", 3, "Number of consecutive stable restore checks required before placing the player back on the boat.");
			ServerRegistryScanInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Server", "ServerRegistryScanInterval", 5f, "Seconds between server-side scans of loaded mobile ships for the persistent ship registry.");
			((BaseUnityPlugin)this).Logger.LogInfo((object)"ValheimRaftBedFix loading");
			_harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("ValheimRaftBedFix loaded | " + $"Debug={DebugMode.Value} | " + $"MaxRetries={MaxRestoreRetries.Value} | " + $"RetryDelay={RetryDelaySeconds.Value:F2}s | " + $"CenterMessages={EnableCenterMessages.Value}"));
		}

		private void Update()
		{
			MobileShipRegistry.Update();
			UpdateServerShipRegistry();
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && !TryHandlePendingMobileRestore(localPlayer) && !(Time.time < _nextShipSampleTime))
			{
				_nextShipSampleTime = Time.time + 1f;
				SampleShipLogoutState(localPlayer);
			}
		}

		private void UpdateServerShipRegistry()
		{
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || Time.time < _nextServerRegistryScanTime)
			{
				return;
			}
			float num = ((ServerRegistryScanInterval != null) ? Mathf.Max(1f, ServerRegistryScanInterval.Value) : 5f);
			_nextServerRegistryScanTime = Time.time + num;
			try
			{
				MobileShipDetector.ScanLoadedShipsForRegistry();
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)("Server ship registry scan failed: " + ex.Message));
				}
			}
		}

		private bool TryHandlePendingMobileRestore(Player player)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: 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_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: 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_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: 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_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			if (!MobileSpawnData.TryGetMobileLogout(player, out var shipGuid, out var localOffset))
			{
				if (_restoreState != 0)
				{
					FinishRestore(player, RestoreState.FailedFallback);
				}
				RestoreInProgress = false;
				return false;
			}
			if (_restoreState == RestoreState.None || !string.Equals(_activeShipGuid, shipGuid, StringComparison.OrdinalIgnoreCase))
			{
				BeginRestore(player, shipGuid, localOffset);
			}
			RestoreInProgress = true;
			ApplyPlayerSuspension(player, _activeHoldPosition, forceTeleport: false);
			if (Time.time >= _nextRestoreMessageTime)
			{
				ShowCenterMessage("Waiting for mobile boat to load...");
				_nextRestoreMessageTime = Time.time + 3f;
			}
			ForceLoadStoredShipZone(player, shipGuid);
			if (Time.time < _nextRestoreAttemptTime)
			{
				return true;
			}
			_nextRestoreAttemptTime = Time.time + Mathf.Max(0.1f, RetryDelaySeconds.Value);
			_restoreAttempts++;
			DebugLog($"Ship restore attempt {_restoreAttempts}/{MaxRestoreRetries.Value} " + "for ship GUID " + shipGuid);
			if (!MobileShipDetector.TryResolveShipByGuid(shipGuid, out var vehicleRoot, out var vehicleView))
			{
				if (_restoreAttempts >= MaxRestoreRetries.Value)
				{
					RestoreViaFallback(player);
				}
				return true;
			}
			_restoreState = RestoreState.Restoring;
			MobileShipDetector.RegisterShip(vehicleView, vehicleRoot);
			if (!IsVehicleStableForRestore(vehicleRoot))
			{
				return true;
			}
			Vector3 position = vehicleRoot.TransformPoint(localOffset);
			position = MakeRestorePositionSafe(position);
			Quaternion rotation = vehicleRoot.rotation;
			DebugLog($"Restoring player to ship GUID {shipGuid} at {position}");
			TeleportPlayer(player, position, rotation);
			MobileSpawnData.ClearMobileLogout(player);
			ShowCenterMessage("Restored position on mobile boat.");
			_trackingBlockedUntil = Time.time + PostRestoreTrackingBlockSeconds.Value;
			FinishRestore(player, RestoreState.Success);
			return true;
		}

		private void BeginRestore(Player player, string shipGuid, Vector3 localOffset)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: 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)
			_restoreState = RestoreState.Waiting;
			_activeShipGuid = shipGuid;
			_activeLocalOffset = localOffset;
			_restoreAttempts = 0;
			_stableFrames = 0;
			_hasLastRootY = false;
			_nextRestoreAttemptTime = 0f;
			_nextRestoreMessageTime = 0f;
			_nextZoneLoadPulseTime = 0f;
			MobileShipRegistry.RequestShipRecord(shipGuid);
			_activeHoldPosition = BuildHoldingPosition(player, shipGuid);
			RestoreInProgress = true;
			ApplyPlayerSuspension(player, _activeHoldPosition, forceTeleport: true);
			ShowCenterMessage("Waiting for mobile boat to load...");
			DebugLog("Starting mobile restore for ship GUID " + shipGuid + "; " + $"holding at {_activeHoldPosition}, local offset {localOffset}");
		}

		private Vector3 BuildHoldingPosition(Player player, string shipGuid)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: 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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			if (MobileShipDetector.TryGetRegisteredShipPosition(shipGuid, out var position, out var _))
			{
				return new Vector3(position.x, HoldingHeight.Value, position.z);
			}
			if (MobileSpawnData.TryGetStoredShipPosition(player, out var position2))
			{
				return new Vector3(position2.x, HoldingHeight.Value, position2.z);
			}
			return new Vector3(TemporaryHoldingPosition.x, HoldingHeight.Value, TemporaryHoldingPosition.z);
		}

		private void ForceLoadStoredShipZone(Player player, string shipGuid)
		{
			//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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			if (Time.time < _nextZoneLoadPulseTime)
			{
				return;
			}
			_nextZoneLoadPulseTime = Time.time + 2f;
			MobileShipRegistry.RequestShipRecord(shipGuid);
			Vector3 activeHoldPosition = _activeHoldPosition;
			Vector3 position2;
			if (MobileShipDetector.TryGetRegisteredShipPosition(shipGuid, out var position, out var _))
			{
				((Vector3)(ref activeHoldPosition))..ctor(position.x, _activeHoldPosition.y, position.z);
				Vector3 val = activeHoldPosition - _activeHoldPosition;
				if (((Vector3)(ref val)).sqrMagnitude > 4f)
				{
					_activeHoldPosition = activeHoldPosition;
					ApplyPlayerSuspension(player, _activeHoldPosition, forceTeleport: true);
				}
			}
			else if (MobileSpawnData.TryGetStoredShipPosition(player, out position2))
			{
				((Vector3)(ref activeHoldPosition))..ctor(position2.x, _activeHoldPosition.y, position2.z);
			}
			TrySetNetworkReferencePosition(activeHoldPosition);
			TryPulseZoneSystem(activeHoldPosition);
		}

		private bool IsVehicleStableForRestore(Transform vehicleRoot)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)vehicleRoot == (Object)null)
			{
				return false;
			}
			if (!MobileShipDetector.IsShipStable(vehicleRoot))
			{
				_stableFrames = 0;
				_hasLastRootY = false;
				return false;
			}
			float y = vehicleRoot.position.y;
			if (_hasLastRootY && Mathf.Abs(y - _lastRootY) > 0.2f)
			{
				_stableFrames = 0;
				_lastRootY = y;
				return false;
			}
			_lastRootY = y;
			_hasLastRootY = true;
			_stableFrames++;
			return _stableFrames >= Mathf.Max(1, StableFramesRequired.Value);
		}

		private void RestoreViaFallback(Player player)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			if (TryRestoreToStoredBed(player))
			{
				MobileSpawnData.ClearMobileLogout(player);
				_trackingBlockedUntil = Time.time + PostRestoreTrackingBlockSeconds.Value;
				FinishRestore(player, RestoreState.Success);
				return;
			}
			ShowCenterMessage("Could not locate your mobile boat. Returning to safe fallback.");
			Vector3 safeOriginFallback = GetSafeOriginFallback();
			MobileSpawnData.ClearMobileLogout(player);
			if (MobileSpawnData.TryGetBedGuid(player, out var _))
			{
				MobileSpawnData.ClearStoredBed(player);
			}
			TeleportPlayer(player, safeOriginFallback, Quaternion.identity);
			_trackingBlockedUntil = Time.time + PostRestoreTrackingBlockSeconds.Value;
			FinishRestore(player, RestoreState.FailedFallback);
		}

		private bool TryRestoreToStoredBed(Player player)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: 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)
			if (!MobileSpawnData.TryGetBedGuid(player, out var bedGuid))
			{
				return false;
			}
			if (!MobileSpawnData.TryResolveBedByGuid(bedGuid, out var bed, out var _))
			{
				DebugLog("Stored bed GUID " + bedGuid + " was not found during validation.");
				return false;
			}
			Vector3 position = MakeRestorePositionSafe(((Component)bed).transform.position + Vector3.up * 1.5f);
			Quaternion rotation = ((Component)bed).transform.rotation;
			DebugLog("Fallback bed restore resolved bed GUID " + bedGuid + " " + $"at {((Component)bed).transform.position}");
			TeleportPlayer(player, position, rotation);
			ShowCenterMessage("Restored to mobile bed.");
			return true;
		}

		private void FinishRestore(Player player, RestoreState finalState)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			_restoreState = finalState;
			RestoreInProgress = false;
			ReleasePlayerSuspension(player);
			_activeShipGuid = null;
			_activeLocalOffset = Vector3.zero;
			_stableFrames = 0;
			_hasLastRootY = false;
			_nextRestoreAttemptTime = 0f;
			_nextRestoreMessageTime = 0f;
			_nextZoneLoadPulseTime = 0f;
			_restoreState = RestoreState.None;
		}

		private void ApplyPlayerSuspension(Player player, Vector3 holdPosition, bool forceTeleport)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: 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_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			Rigidbody component = ((Component)player).GetComponent<Rigidbody>();
			if ((Object)(object)_suspendedBody == (Object)null && (Object)(object)component != (Object)null)
			{
				_suspendedBody = component;
				_savedUseGravity = component.useGravity;
			}
			if ((Object)(object)component != (Object)null)
			{
				component.useGravity = false;
				component.linearVelocity = Vector3.zero;
				component.angularVelocity = Vector3.zero;
			}
			SetPlayerGodMode(player, enabled: true);
			if (!forceTeleport)
			{
				Vector3 val = ((Component)player).transform.position - holdPosition;
				if (!(((Vector3)(ref val)).sqrMagnitude > 625f))
				{
					((Component)player).transform.position = holdPosition;
					if ((Object)(object)component != (Object)null)
					{
						component.position = holdPosition;
					}
					return;
				}
			}
			TeleportPlayer(player, holdPosition, ((Component)player).transform.rotation);
		}

		private void ReleasePlayerSuspension(Player player)
		{
			//IL_0025: 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)
			if ((Object)(object)_suspendedBody != (Object)null)
			{
				_suspendedBody.useGravity = _savedUseGravity;
				_suspendedBody.linearVelocity = Vector3.zero;
				_suspendedBody.angularVelocity = Vector3.zero;
				_suspendedBody = null;
			}
			if ((Object)(object)player != (Object)null)
			{
				SetPlayerGodMode(player, enabled: false);
			}
		}

		private void SetPlayerGodMode(Player player, bool enabled)
		{
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			try
			{
				if ((object)_godModeField == null)
				{
					_godModeField = typeof(Character).GetField("m_godMode", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				}
				if (_godModeField == null || _godModeField.FieldType != typeof(bool))
				{
					return;
				}
				if (enabled)
				{
					if (!_savedHadGodModeField)
					{
						_savedGodMode = (bool)_godModeField.GetValue(player);
						_savedHadGodModeField = true;
					}
					_godModeField.SetValue(player, true);
				}
				else if (_savedHadGodModeField)
				{
					_godModeField.SetValue(player, _savedGodMode);
					_savedHadGodModeField = false;
				}
			}
			catch (Exception ex)
			{
				DebugLog("Could not toggle temporary god mode: " + ex.Message);
			}
		}

		private static Vector3 MakeRestorePositionSafe(Vector3 position)
		{
			//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_001e: 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_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			float num = GetWaterLevel(position) + 1.25f;
			if (position.y < num)
			{
				position.y = num;
			}
			return position + Vector3.up * 0.35f;
		}

		public static Vector3 GetSafeOriginFallback()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: 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_001e: Unknown result type (might be due to invalid IL or missing references)
			Vector3 zero = Vector3.zero;
			float num = 0f;
			if ((Object)(object)ZoneSystem.instance != (Object)null)
			{
				num = ZoneSystem.instance.GetGroundHeight(zero);
			}
			float waterLevel = GetWaterLevel(zero);
			return new Vector3(0f, Mathf.Max(num, waterLevel) + 5f, 0f);
		}

		private static float GetWaterLevel(Vector3 position)
		{
			if ((Object)(object)ZoneSystem.instance == (Object)null)
			{
				return 30f;
			}
			try
			{
				FieldInfo field = typeof(ZoneSystem).GetField("m_waterLevel", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null && field.FieldType == typeof(float))
				{
					return (float)field.GetValue(ZoneSystem.instance);
				}
			}
			catch
			{
			}
			return 30f;
		}

		private static void TeleportPlayer(Player player, Vector3 position, Quaternion rotation)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)player == (Object)null))
			{
				((Character)player).TeleportTo(position, rotation, true);
			}
		}

		private static void TrySetNetworkReferencePosition(Vector3 position)
		{
			//IL_000a: 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)
			TryInvokeReferencePosition(ZNet.instance, "SetReferencePosition", position);
			TryInvokeReferencePosition(ZNetScene.instance, "SetReferencePosition", position);
		}

		private static void TryPulseZoneSystem(Vector3 position)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZoneSystem.instance == (Object)null)
			{
				return;
			}
			TryInvokeReferencePosition(ZoneSystem.instance, "SetReferencePosition", position);
			try
			{
				ZoneSystem.instance.GetGroundHeight(position);
			}
			catch
			{
			}
		}

		private static void TryInvokeReferencePosition(object instance, string methodName, Vector3 position)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			if (instance == null)
			{
				return;
			}
			try
			{
				instance.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(Vector3) }, null)?.Invoke(instance, new object[1] { position });
			}
			catch
			{
			}
		}

		private void SampleShipLogoutState(Player player)
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: 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_0090: 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_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || RestoreInProgress || _restoreState != 0 || Time.time < _trackingBlockedUntil)
			{
				return;
			}
			try
			{
				if (!MobileShipDetector.TryFindVehicleRoot(player, out var vehicleRoot, out var vehicleView))
				{
					return;
				}
				if (!MobileShipDetector.IsShipStable(vehicleRoot))
				{
					DebugLog("Ship unstable; skipping logout sampling.");
					return;
				}
				string orCreateShipGuid = MobileShipDetector.GetOrCreateShipGuid(vehicleView);
				if (string.IsNullOrWhiteSpace(orCreateShipGuid))
				{
					return;
				}
				Vector3 val = vehicleRoot.InverseTransformPoint(((Component)player).transform.position);
				if (((Vector3)(ref val)).sqrMagnitude < 0.0001f)
				{
					Vector3 val2 = vehicleRoot.position - ((Component)player).transform.position;
					if (((Vector3)(ref val2)).sqrMagnitude < 0.0001f)
					{
						DebugLog("Skipping suspicious zero-offset ship sample.");
						return;
					}
				}
				MobileShipDetector.RegisterShip(vehicleView, vehicleRoot);
				MobileSpawnData.SetMobileLogout(player, orCreateShipGuid, val, vehicleRoot.position);
				if (MobileSpawnData.TryFindNearbyBed(player, 10f, out var nearbyBed))
				{
					string orCreateBedGuid = MobileSpawnData.GetOrCreateBedGuid(nearbyBed);
					if (!string.IsNullOrWhiteSpace(orCreateBedGuid))
					{
						MobileSpawnData.SetBed(player, orCreateBedGuid);
					}
				}
				DebugLog("Tracked mobile logout on ship GUID " + orCreateShipGuid + ", " + $"local offset {val}, ship position {vehicleRoot.position}");
			}
			catch (Exception arg)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)$"Ship logout tracking failed: {arg}");
				}
			}
		}

		public static bool ShouldSuppressDamage(Character character)
		{
			if (!RestoreInProgress)
			{
				return false;
			}
			if ((Object)(object)character == (Object)null)
			{
				return false;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return false;
			}
			return character == localPlayer;
		}

		public static void DebugLog(string message)
		{
			if (DebugMode != null && DebugMode.Value)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)message);
				}
			}
		}

		public static void ShowCenterMessage(string message)
		{
			if (EnableCenterMessages == null || EnableCenterMessages.Value)
			{
				MessageHud instance = MessageHud.instance;
				if (instance != null)
				{
					instance.ShowMessage((MessageType)2, message, 0, (Sprite)null, false);
				}
			}
		}

		private void OnDestroy()
		{
			ReleasePlayerSuspension(Player.m_localPlayer);
			MobileShipRegistry.FlushServerRegistryIfDirty();
			_harmony.UnpatchSelf();
			Instance = null;
			Log = null;
		}
	}
	[HarmonyPatch(typeof(Character), "Damage", new Type[] { typeof(HitData) })]
	internal static class RestoreDamageSuppressionPatch
	{
		private static bool Prefix(Character __instance)
		{
			return !ValheimRaftBedFixPlugin.ShouldSuppressDamage(__instance);
		}
	}
}
namespace ValheimRaftBedFix.Patches
{
	[HarmonyPatch(typeof(Bed), "Interact")]
	public static class BedClaimPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Bed __instance, Humanoid human, bool repeat, bool alt, bool __result)
		{
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			ManualLogSource log = ValheimRaftBedFixPlugin.Log;
			if (log != null)
			{
				log.LogInfo((object)"BedClaimPatch fired");
			}
			if (repeat)
			{
				ManualLogSource log2 = ValheimRaftBedFixPlugin.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)"BedClaimPatch exit: repeat interaction");
				}
				return;
			}
			if ((Object)(object)__instance == (Object)null)
			{
				ManualLogSource log3 = ValheimRaftBedFixPlugin.Log;
				if (log3 != null)
				{
					log3.LogWarning((object)"BedClaimPatch exit: __instance was null");
				}
				return;
			}
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				ManualLogSource log4 = ValheimRaftBedFixPlugin.Log;
				if (log4 != null)
				{
					log4.LogWarning((object)"BedClaimPatch exit: Player.m_localPlayer was null");
				}
				return;
			}
			if ((Object)(object)human != (Object)(object)Player.m_localPlayer)
			{
				ManualLogSource log5 = ValheimRaftBedFixPlugin.Log;
				if (log5 != null)
				{
					log5.LogInfo((object)"BedClaimPatch exit: human != localPlayer");
				}
				return;
			}
			ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null)
			{
				ManualLogSource log6 = ValheimRaftBedFixPlugin.Log;
				if (log6 != null)
				{
					log6.LogWarning((object)"BedClaimPatch exit: no ZNetView");
				}
				return;
			}
			if (!component.IsValid())
			{
				ManualLogSource log7 = ValheimRaftBedFixPlugin.Log;
				if (log7 != null)
				{
					log7.LogWarning((object)"BedClaimPatch exit: invalid ZNetView");
				}
				return;
			}
			ZDO zDO = component.GetZDO();
			if (zDO == null)
			{
				ManualLogSource log8 = ValheimRaftBedFixPlugin.Log;
				if (log8 != null)
				{
					log8.LogWarning((object)"BedClaimPatch exit: ZDO was null");
				}
				return;
			}
			string text = zDO.GetString("ValheimRaftBedFix_BedGuid", "");
			if (string.IsNullOrWhiteSpace(text))
			{
				text = Guid.NewGuid().ToString("N");
				zDO.Set("ValheimRaftBedFix_BedGuid", text);
				ManualLogSource log9 = ValheimRaftBedFixPlugin.Log;
				if (log9 != null)
				{
					log9.LogInfo((object)$"Generated new persistent bed GUID {text} for ZDO {zDO.m_uid}");
				}
			}
			else
			{
				ManualLogSource log10 = ValheimRaftBedFixPlugin.Log;
				if (log10 != null)
				{
					log10.LogInfo((object)$"Found existing persistent bed GUID {text} on ZDO {zDO.m_uid}");
				}
			}
			MobileSpawnData.SetBed(Player.m_localPlayer, text);
			ManualLogSource log11 = ValheimRaftBedFixPlugin.Log;
			if (log11 != null)
			{
				log11.LogInfo((object)("Saved player mobile bed GUID " + text));
			}
		}
	}
	internal static class RespawnPatch
	{
	}
}