Decompiled source of BasedItemSaving v1.2.0

BasedItemSaving.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BasedItemSaving.API;
using BasedItemSaving.Compat;
using BasedItemSaving.Saving;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using Dawn;
using Dawn.Internal;
using HarmonyLib;
using HatLib;
using HatLib.Tools;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json.Linq;
using SelfSortingStorage.Cupboard;
using Unity.Netcode;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("com.github.teamxiaolan.dawnlib")]
[assembly: IgnoresAccessChecksTo("com.github.teamxiaolan.dawnlib.dusk")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("BasedItemSaving")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.2.0.0")]
[assembly: AssemblyInformationalVersion("1.2.0")]
[assembly: AssemblyProduct("BasedItemSaving")]
[assembly: AssemblyTitle("BasedItemSaving")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.2.0.0")]
[module: UnverifiableCode]
[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 BasedItemSaving
{
	internal static class ConfigManager
	{
		internal static readonly HashSet<NamespacedKey<DawnUnlockableItemInfo>> ExcludedUnlockables = new HashSet<NamespacedKey<DawnUnlockableItemInfo>>();

		internal static ConfigEntry<bool> SaveCruiserItems = null;

		internal static ConfigEntry<bool> VerboseLogging = null;

		internal static void Bind(ConfigFile config)
		{
			VerboseLogging = config.Bind<bool>("Internal", "Verbose Logging", false, "Enables verbose logging for debugging purposes");
			SaveCruiserItems = config.Bind<bool>("Item Saving", "Save Cruiser Items", true, "Save and restore items inside the company cruiser");
			ConfigEntry<string> exclusions = config.Bind<string>("Item Saving", "Excluded Unlockables", "misideitems:houseteleporter,melaniepocketroom:melmstockroomzero, melaniepocketroom:melmbedroomzero, melaniepocketroom:melmbedroomone, unknown_lib:basement, unknown_lib:house, unknown_lib:poolroom, unknown_lib:garden,  melaniecrafting:workbench, melaniecrafting:crucible,  melaniemeliciouscooked:grinder, melaniemeliciouscooked:blender, melaniemeliciouscooked:crockpot, melaniemeliciouscooked:toasteroven, melaniemeliciouscooked:portablegrill, melaniemeliciouscooked:oven, melaniemeliciouscooked:minifridge, melaniemeliciouscooked:plantera, melaniemeliciouscooked:planterb, melaniemeliciouscooked:planterc, melaniemeliciouscooked:trellisa, melaniemeliciouscooked:trellisb, melaniemeliciouscooked:plantershelfa,  melaniemeliciouscooked:barrela, melaniemeliciouscooked:barrelb, melaniemeliciouscooked:wineracka, melaniemeliciouscooked:beeboxa, melaniemeliciouscooked:pota, melaniemeliciouscooked:icecreammaker, melaniemeliciouscooked:seedmaker", "Unlockables that should be ignored and excluded from item reparenting");
			ReadExclusions(exclusions.Value);
			exclusions.SettingChanged += delegate
			{
				ReadExclusions(exclusions.Value);
			};
		}

		private static void ReadExclusions(string value)
		{
			ExcludedUnlockables.Clear();
			string[] array = value.Split(',', StringSplitOptions.RemoveEmptyEntries);
			NamespacedKey val = default(NamespacedKey);
			foreach (string text in array)
			{
				string text2 = text.Trim();
				if (NamespacedKey.TryParse(text2, ref val))
				{
					ExcludedUnlockables.Add(val.AsTyped<DawnUnlockableItemInfo>());
				}
				else
				{
					Log.Warn("Skipped invalid excluded unlockable key " + text2);
				}
			}
		}
	}
	internal static class Log
	{
		internal static void Debug(string message)
		{
			Plugin.Mls.LogDebug((object)message);
		}

		internal static void Info(string message)
		{
			Plugin.Mls.LogInfo((object)message);
		}

		internal static void Warn(string message)
		{
			Plugin.Mls.LogWarning((object)message);
		}

		internal static void Error(string message)
		{
			Plugin.Mls.LogError((object)message);
		}

		internal static void Fatal(string message)
		{
			Plugin.Mls.LogFatal((object)message);
		}
	}
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("MrHat.BasedItemSaving", "BasedItemSaving", "1.2.0")]
	internal class Plugin : BaseUnityPlugin
	{
		private const string ModGuid = "MrHat.BasedItemSaving";

		private const string ModName = "BasedItemSaving";

		private const string ModVersion = "1.2.0";

		private const string SelfSortingStorageGuid = "zigzag.SelfSortingStorage";

		private const string PocketRoomsLibGuid = "MelanieMelicious.pocketRoomLib";

		private const string V55CruiserGuid = "scandal.v55cruiser";

		internal static ManualLogSource Mls;

		internal static readonly bool HasSelfSortingStorage = Chainloader.PluginInfos.ContainsKey("zigzag.SelfSortingStorage");

		internal static readonly bool HasPocketRoomsLib = Chainloader.PluginInfos.ContainsKey("MelanieMelicious.pocketRoomLib");

		internal static readonly bool HasV55Cruiser = Chainloader.PluginInfos.ContainsKey("scandal.v55cruiser");

		private void Awake()
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			Mls = Logger.CreateLogSource("MrHat.BasedItemSaving");
			if (DawnConfig.DisableDawnItemSaving.Value)
			{
				Log.Error("BasedItemSaving could not start because DawnLib item saving is disabled");
				return;
			}
			ConfigManager.Bind(((BaseUnityPlugin)this).Config);
			if (HasSelfSortingStorage)
			{
				new Harmony("MrHat.BasedItemSaving").PatchAll(typeof(SelfSortingStorageCompat));
				Log.Info("SelfSortingStorage compatibility enabled");
			}
			if (HasPocketRoomsLib)
			{
				Log.Info("PocketRoomsLib compatibility enabled");
			}
			ShipItemSaving.Init();
			if (ConfigManager.VerboseLogging.Value)
			{
				DawnItemSaveEvents.OnCollectExtraSaveData += VerboseLogging.ItemSaved;
				RoundEvents.OnRoundStart += VerboseLogging.CollidersFound;
				Utility.ItemRestored = (Action<GrabbableObject, SavedItemPose, Collider>)Delegate.Combine(Utility.ItemRestored, new Action<GrabbableObject, SavedItemPose, Collider>(VerboseLogging.ItemRestored));
				Utility.ItemReturningToShip = (Action<GrabbableObject, SavedItemPose>)Delegate.Combine(Utility.ItemReturningToShip, new Action<GrabbableObject, SavedItemPose>(VerboseLogging.ItemReturningToShip));
				ShipItemSaving.ItemPositionSkipped = (Action<GrabbableObject, string>)Delegate.Combine(ShipItemSaving.ItemPositionSkipped, new Action<GrabbableObject, string>(VerboseLogging.ItemPositionSkipped));
				ShipItemSaving.ItemRemoved = (Action<GrabbableObject, string>)Delegate.Combine(ShipItemSaving.ItemRemoved, new Action<GrabbableObject, string>(VerboseLogging.ItemRemoved));
				ShipItemSaving.ItemValueRestored = (Action<GrabbableObject, string, float>)Delegate.Combine(ShipItemSaving.ItemValueRestored, new Action<GrabbableObject, string, float>(VerboseLogging.ItemValueRestored));
				ShipItemSaving.ItemsSynced = (Action<ItemAssignmentList, ulong?, bool>)Delegate.Combine(ShipItemSaving.ItemsSynced, new Action<ItemAssignmentList, ulong?, bool>(VerboseLogging.ItemsSynced));
				if (HasPocketRoomsLib)
				{
					RoundEvents.OnRoundStart += VerboseLogging.PocketRoomsCollidersFound;
				}
			}
			Log.Info("BasedItemSaving 1.2.0 loaded");
		}
	}
	internal static class Utility
	{
		internal static Action<GrabbableObject, SavedItemPose, Collider?>? ItemRestored;

		internal static Action<GrabbableObject, SavedItemPose>? ItemReturningToShip;

		private static Collider? SavingColliderAt(Vector3 position)
		{
			//IL_0000: 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_0043: 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_00c5: 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_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			VehicleController val = CruiserAt(position);
			if ((Object)(object)val != (Object)null)
			{
				return val.physicsRegion.itemDropCollider;
			}
			Collider shipInnerRoomBounds = StartOfRound.Instance.shipInnerRoomBounds;
			Bounds bounds;
			if (shipInnerRoomBounds.enabled && ((Component)shipInnerRoomBounds).gameObject.activeInHierarchy)
			{
				bounds = shipInnerRoomBounds.bounds;
				if (((Bounds)(ref bounds)).Contains(position))
				{
					return shipInnerRoomBounds;
				}
			}
			foreach (Collider validItemSpawningCollider in ItemSaveDataHandler.ValidItemSpawningColliders)
			{
				if (!((Object)(object)validItemSpawningCollider != (Object)null) || validItemSpawningCollider == null || !validItemSpawningCollider.enabled)
				{
					continue;
				}
				GameObject gameObject = ((Component)validItemSpawningCollider).gameObject;
				if (gameObject != null && gameObject.activeInHierarchy)
				{
					bounds = validItemSpawningCollider.bounds;
					if (((Bounds)(ref bounds)).Contains(position))
					{
						return validItemSpawningCollider;
					}
				}
			}
			if (Plugin.HasPocketRoomsLib)
			{
				return PocketRoomsLibCompat.SavingColliderAt(position);
			}
			return null;
		}

		internal static VehicleController? CruiserAt(Vector3 position, Transform? itemTransform = null)
		{
			//IL_006c: 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_0072: Unknown result type (might be due to invalid IL or missing references)
			VehicleController attachedVehicle = StartOfRound.Instance.attachedVehicle;
			if ((Object)(object)attachedVehicle == (Object)null || !((NetworkBehaviour)attachedVehicle).IsSpawned)
			{
				return null;
			}
			if (!ConfigManager.SaveCruiserItems.Value || (attachedVehicle.vehicleID == 0 && Plugin.HasV55Cruiser))
			{
				return null;
			}
			if ((Object)(object)itemTransform != (Object)null)
			{
				if (itemTransform.IsChildOf(((Component)attachedVehicle).transform))
				{
					return attachedVehicle;
				}
				return null;
			}
			Collider itemDropCollider = attachedVehicle.physicsRegion.itemDropCollider;
			if ((Object)(object)itemDropCollider != (Object)null && itemDropCollider.ClosestPoint(position) == position)
			{
				return attachedVehicle;
			}
			return null;
		}

		internal static bool PlaceShipPose(GrabbableObject item, SavedItemPose pose, ItemAssignmentList assignments)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: 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_006d: 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_0086: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)item == (Object)null)
			{
				return false;
			}
			StartOfRound instance = StartOfRound.Instance;
			Collider val = SavingColliderAt(pose.worldPosition);
			bool flag = pose.parentKind != ItemParentKind.Ship || (Object)(object)val == (Object)null;
			if (flag)
			{
				ItemReturningToShip?.Invoke(item, pose);
				pose.parentKind = ItemParentKind.Ship;
				pose.worldPosition = item.GetItemFloorPosition(instance.elevatorTransform.TransformPoint(new Vector3(0f, 0f, -7f)));
				item.isInElevator = true;
				item.isInShipRoom = true;
				val = SavingColliderAt(pose.worldPosition);
			}
			if (!GrabbablePlacement.Place(item, instance.elevatorTransform, pose.worldPosition, pose.worldRotation))
			{
				return false;
			}
			if (flag)
			{
				assignments.returnedToShipCount++;
			}
			if ((Object)(object)val != (Object)null)
			{
				assignments.colliderCounts.TryGetValue(((Object)val).name, out var value);
				assignments.colliderCounts[((Object)val).name] = value + 1;
			}
			ItemRestored?.Invoke(item, pose, val);
			return true;
		}

		internal static string? FurnitureParentPath(Transform root, Transform parent)
		{
			if ((Object)(object)parent == (Object)(object)root)
			{
				return string.Empty;
			}
			List<int> list = new List<int>();
			Transform val = parent;
			while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root)
			{
				list.Add(val.GetSiblingIndex());
				val = val.parent;
			}
			if ((Object)(object)val != (Object)(object)root)
			{
				return null;
			}
			list.Reverse();
			return string.Join("/", list);
		}

		internal static Transform? FurnitureParentFromPath(Transform root, string path)
		{
			Transform val = root;
			if (string.IsNullOrWhiteSpace(path))
			{
				return val;
			}
			string[] array = path.Split('/');
			foreach (string s in array)
			{
				if (!int.TryParse(s, out var result) || result < 0 || result >= val.childCount)
				{
					return null;
				}
				val = val.GetChild(result);
			}
			return val;
		}

		internal static bool PlaceFurniturePose(GrabbableObject? item, Transform parent, SavedItemPose pose, ItemAssignmentList assignments)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: 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_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: 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_0072: 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_008a: 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_0099: 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_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: 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)
			if ((Object)(object)item == (Object)null)
			{
				return false;
			}
			Vector3 val = parent.TransformPoint(pose.localPosition);
			Collider val2 = SavingColliderAt(val);
			if ((Object)(object)val2 == (Object)null && pose.parentKind != ItemParentKind.Cruiser)
			{
				pose.parentKind = ItemParentKind.Ship;
				pose.worldPosition = val;
				return PlaceShipPose(item, pose, assignments);
			}
			if (!GrabbablePlacement.Place(item, parent, val, parent.rotation * pose.localRotation))
			{
				return false;
			}
			((Component)item).transform.SetLocalPositionAndRotation(pose.localPosition, pose.localRotation);
			item.targetFloorPosition = pose.localPosition;
			item.startFallingPosition = pose.localPosition + Vector3.up * 0.0001f;
			if ((Object)(object)val2 != (Object)null)
			{
				assignments.colliderCounts.TryGetValue(((Object)val2).name, out var value);
				assignments.colliderCounts[((Object)val2).name] = value + 1;
			}
			ItemRestored?.Invoke(item, pose, val2);
			return true;
		}
	}
	internal static class VerboseLogging
	{
		internal static void ItemSaved(GrabbableObject item, JObject extraData)
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: 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)
			string itemName = item.itemProperties.itemName;
			JToken val = extraData[((object)ShipItemSaving.RemoveOnLoadKey).ToString()];
			if (val != null)
			{
				Log.Debug("Marked " + itemName + " for removal on load because it is " + Extensions.Value<string>((IEnumerable<JToken>)val));
				return;
			}
			JToken val2 = extraData[((object)ShipItemSaving.PoseKey).ToString()];
			if (val2 != null)
			{
				SavedItemPose savedItemPose = val2.ToObject<SavedItemPose>();
				if (savedItemPose.parentKind == ItemParentKind.BeltBag)
				{
					Log.Debug("Saving " + itemName + " inside a belt bag");
				}
				else if (savedItemPose.parentKind == ItemParentKind.Cruiser)
				{
					Log.Debug($"Saving {itemName} inside the cruiser at local position {savedItemPose.localPosition:F3} with rotation {((Quaternion)(ref savedItemPose.localRotation)).eulerAngles:F1}");
				}
				else if (savedItemPose.parentKind == ItemParentKind.Furniture)
				{
					Log.Debug($"Saving {itemName} on {savedItemPose.furnitureKey} at local position {savedItemPose.localPosition:F3} with rotation {((Quaternion)(ref savedItemPose.localRotation)).eulerAngles:F1} and parent path '{savedItemPose.furnitureParentPath}'");
				}
				else
				{
					Log.Debug($"Saving {itemName} at {savedItemPose.worldPosition:F3} with rotation {((Quaternion)(ref savedItemPose.worldRotation)).eulerAngles:F1}");
				}
			}
			else if (item.deactivated)
			{
				Log.Debug("Skipped position saving for " + itemName + " because it is deactivated");
			}
			else if (item.itemUsedUp)
			{
				Log.Debug("Skipped position saving for " + itemName + " because it is used up");
			}
			JToken arg = default(JToken);
			if (extraData.TryGetValue(((object)ShipItemSaving.MeshVariantKey).ToString(), ref arg))
			{
				Log.Debug($"Saving mesh variant {arg} for {itemName}");
			}
			JToken arg2 = default(JToken);
			if (extraData.TryGetValue(((object)ShipItemSaving.MaterialVariantKey).ToString(), ref arg2))
			{
				Log.Debug($"Saving material variant {arg2} for {itemName}");
			}
			JToken val3 = default(JToken);
			if (extraData.TryGetValue(((object)ShipItemSaving.BatteryChargeKey).ToString(), ref val3))
			{
				Log.Debug($"Saving battery charge for {itemName} at {Extensions.Value<float>((IEnumerable<JToken>)val3) * 100f:F1}%");
			}
			JToken val4 = default(JToken);
			if (extraData.TryGetValue(((object)ShipItemSaving.ChemicalFuelKey).ToString(), ref val4))
			{
				Log.Debug($"Saving TZP fuel for {itemName} at {Extensions.Value<float>((IEnumerable<JToken>)val4) * 100f:F1}%");
			}
			JToken val5 = default(JToken);
			if (extraData.TryGetValue(((object)ShipItemSaving.SprayPaintTankKey).ToString(), ref val5))
			{
				Log.Debug($"Saving spray paint/weed killer fuel for {itemName} at {Extensions.Value<float>((IEnumerable<JToken>)val5) * 100f:F1}%");
			}
		}

		internal static void ItemRemoved(GrabbableObject item, string reason)
		{
			Log.Debug("Host removing saved " + item.itemProperties.itemName + " because it was " + reason);
		}

		internal static void ItemPositionSkipped(GrabbableObject item, string reason)
		{
			Log.Debug("Skipped position saving for " + item.itemProperties.itemName + " because " + reason);
		}

		internal static void ItemReturningToShip(GrabbableObject item, SavedItemPose pose)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			string text = "Client";
			if (NetworkManager.Singleton.IsServer)
			{
				text = "Host";
			}
			string text2 = "its saved position is outside the item saving bounds";
			if (pose.parentKind == ItemParentKind.Cruiser)
			{
				text2 = "its saved cruiser could not be used";
			}
			else if (pose.parentKind == ItemParentKind.Furniture)
			{
				text2 = "its saved furniture or parent could not be found";
			}
			Log.Debug($"{text} returning {item.itemProperties.itemName} from {pose.worldPosition:F3} to the middle of the ship because {text2}");
		}

		internal static void ItemRestored(GrabbableObject item, SavedItemPose pose, Collider? collider)
		{
			//IL_00bc: 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)
			string text = "Client";
			if (NetworkManager.Singleton.IsServer)
			{
				text = "Host";
			}
			if (pose.parentKind == ItemParentKind.BeltBag)
			{
				Log.Debug(text + " restored " + item.itemProperties.itemName + " inside a belt bag");
				return;
			}
			string text2 = "in the ship";
			if (pose.parentKind == ItemParentKind.Furniture)
			{
				text2 = "on " + pose.furnitureKey;
			}
			else if (pose.parentKind == ItemParentKind.Cruiser)
			{
				text2 = "in the cruiser";
			}
			string text3 = string.Empty;
			if ((Object)(object)collider != (Object)null)
			{
				text3 = " inside " + ((Object)collider).name;
			}
			Log.Debug($"{text} restored {item.itemProperties.itemName} {text2} at {((Component)item).transform.position:F3} with rotation {((Component)item).transform.eulerAngles:F1}{text3}");
		}

		internal static void ItemValueRestored(GrabbableObject item, string kind, float value)
		{
			string text = "Client";
			if (NetworkManager.Singleton.IsServer)
			{
				text = "Host";
			}
			if (kind == "mesh variant" || kind == "material variant")
			{
				Log.Debug($"{text} restored {kind} {value:F0} for {item.itemProperties.itemName}");
			}
			else
			{
				Log.Debug($"{text} restored {kind} for {item.itemProperties.itemName} to {value * 100f:F1}%");
			}
		}

		internal static void ItemsSynced(ItemAssignmentList assignments, ulong? clientId, bool sent)
		{
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			string text = "Received";
			string text2 = "from the host";
			if (sent)
			{
				text = "Sent";
				text2 = "to all clients";
				if (clientId.HasValue)
				{
					text2 = $"to client {clientId.Value}";
				}
			}
			foreach (ItemAssignment item in assignments.items)
			{
				SavedItemPose pose = item.pose;
				if (pose.parentKind == ItemParentKind.BeltBag)
				{
					Log.Debug(text + " " + item.itemName + " inside a belt bag " + text2);
				}
				else if (pose.parentKind == ItemParentKind.Cruiser)
				{
					Log.Debug($"{text} {item.itemName} inside the cruiser at local position {pose.localPosition:F3} with rotation {((Quaternion)(ref pose.localRotation)).eulerAngles:F1} {text2}");
				}
				else if (pose.parentKind == ItemParentKind.Furniture)
				{
					Log.Debug($"{text} {item.itemName} on {pose.furnitureKey} at local position {pose.localPosition:F3} with rotation {((Quaternion)(ref pose.localRotation)).eulerAngles:F1} and parent path '{pose.furnitureParentPath}' {text2}");
				}
				else
				{
					Log.Debug($"{text} {item.itemName} position {pose.worldPosition:F3} with rotation {((Quaternion)(ref pose.worldRotation)).eulerAngles:F1} {text2}");
				}
			}
			foreach (ItemAssignment item2 in assignments.items.Concat(assignments.variants))
			{
				if (item2.meshVariant >= 0)
				{
					Log.Debug($"{text} mesh variant {item2.meshVariant} for {item2.itemName} {text2}");
				}
				if (item2.materialVariant >= 0)
				{
					Log.Debug($"{text} material variant {item2.materialVariant} for {item2.itemName} {text2}");
				}
				if (item2.batteryCharge >= 0f)
				{
					Log.Debug($"{text} battery charge for {item2.itemName} at {item2.batteryCharge * 100f:F1}% {text2}");
				}
				if (item2.chemicalFuel >= 0f)
				{
					Log.Debug($"{text} TZP fuel for {item2.itemName} at {item2.chemicalFuel * 100f:F1}% {text2}");
				}
				if (item2.sprayPaintTank >= 0f)
				{
					Log.Debug($"{text} spray paint/weed killer fuel for {item2.itemName} at {item2.sprayPaintTank * 100f:F1}% {text2}");
				}
			}
		}

		internal static void CollidersFound(StartOfRound round)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: 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_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_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: 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_0083: 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_008b: 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_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			Collider shipInnerRoomBounds = round.shipInnerRoomBounds;
			string name = ((Object)shipInnerRoomBounds).name;
			Bounds bounds = shipInnerRoomBounds.bounds;
			object arg = ((Bounds)(ref bounds)).center;
			bounds = shipInnerRoomBounds.bounds;
			Log.Debug($"Found ship bounds {name} at {arg:F3} with size {((Bounds)(ref bounds)).size:F3}");
			foreach (Collider validItemSpawningCollider in ItemSaveDataHandler.ValidItemSpawningColliders)
			{
				if ((Object)(object)validItemSpawningCollider != (Object)null)
				{
					string name2 = ((Object)validItemSpawningCollider).name;
					bounds = validItemSpawningCollider.bounds;
					object arg2 = ((Bounds)(ref bounds)).center;
					bounds = validItemSpawningCollider.bounds;
					Log.Debug($"Found DawnLib item saving bounds {name2} at {arg2:F3} with size {((Bounds)(ref bounds)).size:F3}");
				}
			}
			VehicleController attachedVehicle = round.attachedVehicle;
			if ((Object)(object)attachedVehicle != (Object)null && ((NetworkBehaviour)attachedVehicle).IsSpawned && ConfigManager.SaveCruiserItems.Value && (attachedVehicle.vehicleID != 0 || !Plugin.HasV55Cruiser))
			{
				Collider itemDropCollider = attachedVehicle.physicsRegion.itemDropCollider;
				if ((Object)(object)itemDropCollider != (Object)null)
				{
					object[] obj = new object[4]
					{
						attachedVehicle.vehicleID,
						((Object)itemDropCollider).name,
						null,
						null
					};
					bounds = itemDropCollider.bounds;
					obj[2] = ((Bounds)(ref bounds)).center;
					bounds = itemDropCollider.bounds;
					obj[3] = ((Bounds)(ref bounds)).size;
					Log.Debug(string.Format("Found cruiser {0} item saving bounds {1} at {2:F3} with size {3:F3}", obj));
				}
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		internal static void PocketRoomsCollidersFound(StartOfRound round)
		{
			//IL_002a: 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_0032: 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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			foreach (Collider roomBound in MelShipBoundsCheck.roomBounds)
			{
				if ((Object)(object)roomBound != (Object)null)
				{
					string name = ((Object)roomBound).name;
					Bounds bounds = roomBound.bounds;
					object arg = ((Bounds)(ref bounds)).center;
					bounds = roomBound.bounds;
					Log.Debug($"Found PocketRoomsLib item saving bounds {name} at {arg:F3} with size {((Bounds)(ref bounds)).size:F3}");
				}
			}
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "BasedItemSaving";

		public const string PLUGIN_NAME = "BasedItemSaving";

		public const string PLUGIN_VERSION = "1.2.0";
	}
}
namespace BasedItemSaving.Saving
{
	internal class FurnitureRelationship(PlaceableObjectsSurface surface, PlaceableShipObject shipObject, Transform itemParent, NamespacedKey<DawnUnlockableItemInfo> furnitureKey, string parentPath)
	{
		internal PlaceableObjectsSurface Surface { get; } = surface;

		internal PlaceableShipObject ShipObject { get; } = shipObject;

		internal Transform ItemParent { get; } = itemParent;

		internal NamespacedKey<DawnUnlockableItemInfo> FurnitureKey { get; } = furnitureKey;

		internal string ParentPath { get; } = parentPath;

		internal static FurnitureRelationship? FromItem(GrabbableObject item, out bool surfaceFound)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			surfaceFound = false;
			Transform parent = ((Component)item).transform.parent;
			if ((Object)(object)parent == (Object)null)
			{
				return null;
			}
			NetworkObject componentInParent = ((Component)parent).GetComponentInParent<NetworkObject>();
			if ((Object)(object)componentInParent == (Object)null)
			{
				return null;
			}
			PlaceableObjectsSurface val = SurfaceAt(parent, item, ((Component)item).transform.position, ((Component)componentInParent).GetComponentsInChildren<PlaceableObjectsSurface>(), out surfaceFound);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			PlaceableShipObject componentInChildren = ((Component)val.parentTo).GetComponentInChildren<PlaceableShipObject>(true);
			if ((Object)(object)componentInChildren == (Object)null || (Object)(object)componentInChildren.parentObject == (Object)null)
			{
				return null;
			}
			StartOfRound instance = StartOfRound.Instance;
			int unlockableID = componentInChildren.unlockableID;
			if (unlockableID < 0 || unlockableID >= instance.unlockablesList.unlockables.Count)
			{
				return null;
			}
			UnlockableItem val2 = instance.unlockablesList.unlockables[unlockableID];
			if (val2.inStorage)
			{
				return null;
			}
			DawnUnlockableItemInfo dawnInfo = UnlockableItemExtensions.GetDawnInfo(val2);
			string text = Utility.FurnitureParentPath(((Component)componentInChildren.parentObject).transform, parent);
			if (dawnInfo == null || text == null)
			{
				return null;
			}
			return new FurnitureRelationship(val, componentInChildren, parent, ((DawnBaseInfo<DawnUnlockableItemInfo>)(object)dawnInfo).TypedKey, text);
		}

		internal static bool RestoreParent(StartOfRound round, GrabbableObject item, SavedItemPose pose, out PlaceableShipObject? shipObject, out Transform? parent, out bool unresolved)
		{
			//IL_00e4: 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)
			shipObject = null;
			parent = null;
			unresolved = false;
			NamespacedKey val = default(NamespacedKey);
			if (!NamespacedKey.TryParse(pose.furnitureKey, ref val))
			{
				unresolved = true;
				return false;
			}
			NamespacedKey<DawnUnlockableItemInfo> key = val.AsTyped<DawnUnlockableItemInfo>();
			if (ConfigManager.ExcludedUnlockables.Contains(key))
			{
				return false;
			}
			shipObject = ((IEnumerable<PlaceableShipObject>)UnlockableSaveDataHandler.placeableShipObjects).FirstOrDefault((Func<PlaceableShipObject, bool>)delegate(PlaceableShipObject candidate)
			{
				if (!((Component)candidate).gameObject.activeInHierarchy)
				{
					return false;
				}
				int unlockableID = candidate.unlockableID;
				if (unlockableID < 0 || unlockableID >= round.unlockablesList.unlockables.Count)
				{
					return false;
				}
				UnlockableItem val3 = round.unlockablesList.unlockables[unlockableID];
				return ((object)((DawnBaseInfo<DawnUnlockableItemInfo>)(object)UnlockableItemExtensions.GetDawnInfo(val3))?.TypedKey).Equals((object?)key) ?? false;
			});
			if ((Object)(object)shipObject == (Object)null || (Object)(object)shipObject.parentObject == (Object)null || !((NetworkBehaviour)shipObject.parentObject).IsSpawned || round.unlockablesList.unlockables[shipObject.unlockableID].inStorage || (Object)(object)(parent = Utility.FurnitureParentFromPath(((Component)shipObject.parentObject).transform, pose.furnitureParentPath)) == (Object)null)
			{
				unresolved = true;
				return false;
			}
			bool found;
			PlaceableObjectsSurface val2 = SurfaceAt(parent, item, parent.TransformPoint(pose.localPosition), ((Component)shipObject.parentObject).GetComponentsInChildren<PlaceableObjectsSurface>(true), out found);
			if (!((Object)(object)val2 == (Object)null) && PlaceableObjectsSurfaceRegistry.TryGet(val2, out var registration))
			{
				return registration.AllowAutomaticSaving;
			}
			return true;
		}

		private static PlaceableObjectsSurface? SurfaceAt(Transform parent, GrabbableObject item, Vector3 position, PlaceableObjectsSurface[] surfaces, out bool found)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			PlaceableObjectsSurface[] array = surfaces.Where(delegate(PlaceableObjectsSurface surface)
			{
				if ((Object)(object)surface.parentTo == (Object)null)
				{
					return false;
				}
				Transform val = ((Component)surface.parentTo).transform;
				PlayerPhysicsRegion val2 = ((IEnumerable<PlayerPhysicsRegion>)((Component)val).GetComponentsInChildren<PlayerPhysicsRegion>()).FirstOrDefault((Func<PlayerPhysicsRegion, bool>)((PlayerPhysicsRegion candidate) => ((Behaviour)candidate).isActiveAndEnabled));
				if ((Object)(object)val2 != (Object)null && val2.allowDroppingItems && (Object)(object)val2.physicsTransform != (Object)null)
				{
					val = val2.physicsTransform;
				}
				return (Object)(object)val == (Object)(object)parent;
			}).ToArray();
			found = array.Length != 0;
			if (array.Length < 2)
			{
				return array.FirstOrDefault();
			}
			float offset = item.itemProperties.verticalOffset;
			return array.Where((PlaceableObjectsSurface surface) => (Object)(object)surface.placeableBounds != (Object)null).OrderBy(delegate(PlaceableObjectsSurface surface)
			{
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				//IL_0013: 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_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_002a: 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_0030: Unknown result type (might be due to invalid IL or missing references)
				//IL_0031: Unknown result type (might be due to invalid IL or missing references)
				//IL_0036: Unknown result type (might be due to invalid IL or missing references)
				Collider placeableBounds = surface.placeableBounds;
				Vector3 val = position - ((Component)placeableBounds).transform.up * offset;
				Vector3 val2 = placeableBounds.ClosestPoint(val) - val;
				return ((Vector3)(ref val2)).sqrMagnitude;
			}).FirstOrDefault();
		}
	}
	internal static class ShipItemSaving
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static Action<GrabbableObject, JObject> <0>__ReadItem;

			public static Action<StartOfRound> <1>__RoundStarted;

			public static Action<StartOfRound> <2>__ItemsLoaded;

			public static Action<StartOfRound> <3>__UnlockablesSynced;

			public static Action <4>__GameSaved;

			public static Action<StartOfRound> <5>__RoundCleared;

			public static HatNetMessageHandler <6>__AssignmentsReceived;

			public static HatNetMessageHandler <7>__AssignmentsRequested;
		}

		private static readonly WaitForSecondsRealtime waitForSecondsRealtime1625 = new WaitForSecondsRealtime(16.25f);

		private static readonly WaitForSeconds waitForSeconds05 = new WaitForSeconds(0.5f);

		internal static readonly NamespacedKey PoseKey = NamespacedKey.From("baseditemsaving", "exact_pose");

		private static readonly NamespacedKey ItemIdKey = NamespacedKey.From("baseditemsaving", "item_id");

		internal static readonly NamespacedKey MeshVariantKey = NamespacedKey.From("baseditemsaving", "mesh_variant");

		internal static readonly NamespacedKey MaterialVariantKey = NamespacedKey.From("baseditemsaving", "material_variant");

		internal static readonly NamespacedKey BatteryChargeKey = NamespacedKey.From("baseditemsaving", "battery_charge");

		internal static readonly NamespacedKey ChemicalFuelKey = NamespacedKey.From("baseditemsaving", "chemical_fuel");

		internal static readonly NamespacedKey SprayPaintTankKey = NamespacedKey.From("baseditemsaving", "spray_paint_tank");

		internal static readonly NamespacedKey RemoveOnLoadKey = NamespacedKey.From("baseditemsaving", "remove_on_load");

		private static readonly NamespacedKey AssignmentKey = NamespacedKey.From("baseditemsaving", "item_assignments");

		private static readonly NamespacedKey RequestKey = NamespacedKey.From("baseditemsaving", "item_assignments_request");

		private static readonly Dictionary<GrabbableObject, SavedItemPose> pending = new Dictionary<GrabbableObject, SavedItemPose>();

		private static readonly Dictionary<GrabbableObject, (int Mesh, int Material, float Battery, float Chemical, float SprayPaint)> pendingItemValues = new Dictionary<GrabbableObject, (int, int, float, float, float)>();

		private static readonly Dictionary<string, GrabbableObject> loadedItems = new Dictionary<string, GrabbableObject>();

		private static readonly Dictionary<ulong, ItemAssignment> hostItems = new Dictionary<ulong, ItemAssignment>();

		private static readonly Dictionary<GrabbableObject, JObject> collectedItems = new Dictionary<GrabbableObject, JObject>();

		private static Coroutine? _restoreCoroutine;

		private static bool _unlockablesSynced;

		internal static Action<GrabbableObject, string, float>? ItemValueRestored;

		internal static Action<GrabbableObject, string>? ItemPositionSkipped;

		internal static Action<GrabbableObject, string>? ItemRemoved;

		internal static Action<ItemAssignmentList, ulong?, bool>? ItemsSynced;

		internal static void Init()
		{
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Expected O, but got Unknown
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Expected O, but got Unknown
			DawnItemSaveEvents.OnCollectExtraSaveData += delegate(GrabbableObject item, JObject extraData)
			{
				SaveItem(item, extraData, ItemSaveDataHandler.AllShipItems);
				collectedItems[item] = extraData;
			};
			DawnItemSaveEvents.OnLoadExtraSaveData += ReadItem;
			RoundEvents.OnRoundStart += RoundStarted;
			RoundEvents.OnShipItemsLoaded += ItemsLoaded;
			RoundEvents.OnShipUnlockablesSynced += UnlockablesSynced;
			RoundEvents.OnGameSaved += GameSaved;
			RoundEvents.OnLocalDisconnect += RoundCleared;
			RoundEvents.OnDestroy += RoundCleared;
			NamespacedKey assignmentKey = AssignmentKey;
			object obj = <>O.<6>__AssignmentsReceived;
			if (obj == null)
			{
				HatNetMessageHandler val = AssignmentsReceived;
				<>O.<6>__AssignmentsReceived = val;
				obj = (object)val;
			}
			HatNet.AddClientHandler(assignmentKey, (HatNetMessageHandler)obj);
			NamespacedKey requestKey = RequestKey;
			object obj2 = <>O.<7>__AssignmentsRequested;
			if (obj2 == null)
			{
				HatNetMessageHandler val2 = AssignmentsRequested;
				<>O.<7>__AssignmentsRequested = val2;
				obj2 = (object)val2;
			}
			HatNet.AddServerHandler(requestKey, (HatNetMessageHandler)obj2);
		}

		private static void SaveItem(GrabbableObject item, JObject extraData, GrabbableObject[] items)
		{
			float num = -1f;
			GrabbableObject obj = item;
			TetraChemicalItem val = (TetraChemicalItem)(object)((obj is TetraChemicalItem) ? obj : null);
			if (val != null)
			{
				num = Mathf.Clamp01(val.fuel);
				extraData[((object)ChemicalFuelKey).ToString()] = JToken.op_Implicit(num);
			}
			float num2 = -1f;
			GrabbableObject obj2 = item;
			SprayPaintItem val2 = (SprayPaintItem)(object)((obj2 is SprayPaintItem) ? obj2 : null);
			if (val2 != null)
			{
				num2 = Mathf.Clamp01(val2.sprayCanTank);
				extraData[((object)SprayPaintTankKey).ToString()] = JToken.op_Implicit(num2);
			}
			if (item.deactivated || item.itemUsedUp)
			{
				if (item.deactivated)
				{
					extraData[((object)RemoveOnLoadKey).ToString()] = JToken.op_Implicit("deactivated");
				}
				else if (num < 0f && num2 < 0f)
				{
					extraData[((object)RemoveOnLoadKey).ToString()] = JToken.op_Implicit("used up");
				}
				Forget(item);
				if (NetworkManager.Singleton.IsServer && ((NetworkBehaviour)item).NetworkObject.IsSpawned && (num >= 0f || num2 >= 0f))
				{
					hostItems[((NetworkBehaviour)item).NetworkObjectId] = new ItemAssignment(item, -1, -1, -1f, num, num2);
				}
				return;
			}
			extraData[((object)ItemIdKey).ToString()] = JToken.op_Implicit(((NetworkBehaviour)item).NetworkObjectId.ToString());
			Mesh[] meshVariants = item.itemProperties.meshVariants;
			Material[] materialVariants = item.itemProperties.materialVariants;
			int num3 = -1;
			MeshFilter val3 = default(MeshFilter);
			if (meshVariants != null && ((Component)item).TryGetComponent<MeshFilter>(ref val3))
			{
				num3 = Array.IndexOf(meshVariants, val3.sharedMesh);
				if (num3 >= 0)
				{
					extraData[((object)MeshVariantKey).ToString()] = JToken.op_Implicit(num3);
				}
			}
			int num4 = -1;
			MeshRenderer val4 = default(MeshRenderer);
			if (materialVariants != null && ((Component)item).TryGetComponent<MeshRenderer>(ref val4))
			{
				num4 = Array.IndexOf(materialVariants, ((Renderer)val4).sharedMaterial);
				if (num4 >= 0)
				{
					extraData[((object)MaterialVariantKey).ToString()] = JToken.op_Implicit(num4);
				}
			}
			float num5 = -1f;
			if (item.itemProperties.requiresBattery && item.insertedBattery != null)
			{
				num5 = Mathf.Clamp01(item.insertedBattery.charge);
				extraData[((object)BatteryChargeKey).ToString()] = JToken.op_Implicit(num5);
			}
			BeltBagItem val5 = items.OfType<BeltBagItem>().FirstOrDefault((Func<BeltBagItem, bool>)((BeltBagItem candidate) => candidate.objectsInBag.Contains(item)));
			if ((Object)(object)val5 != (Object)null)
			{
				SavedItemPose savedItemPose = new SavedItemPose
				{
					parentKind = ItemParentKind.BeltBag,
					parentItemId = ((NetworkBehaviour)val5).NetworkObjectId.ToString()
				};
				extraData[((object)PoseKey).ToString()] = (JToken)(object)JObject.Parse(JsonUtility.ToJson((object)savedItemPose));
				Forget(item);
				if (NetworkManager.Singleton.IsServer && ((NetworkBehaviour)item).NetworkObject.IsSpawned && ((NetworkBehaviour)val5).NetworkObject.IsSpawned)
				{
					hostItems[((NetworkBehaviour)item).NetworkObjectId] = new ItemAssignment(item, savedItemPose)
					{
						parentNetworkId = ((NetworkBehaviour)val5).NetworkObjectId,
						meshVariant = num3,
						materialVariant = num4,
						batteryCharge = num5,
						chemicalFuel = num,
						sprayPaintTank = num2
					};
				}
				return;
			}
			FurnitureRelationship furniture;
			VehicleController cruiser;
			SavedItemPose savedItemPose2 = PoseFor(item, out furniture, out cruiser);
			if (savedItemPose2 == null)
			{
				extraData.Remove(((object)PoseKey).ToString());
				Forget(item);
				if (NetworkManager.Singleton.IsServer && ((NetworkBehaviour)item).NetworkObject.IsSpawned && (num3 >= 0 || num4 >= 0 || num5 >= 0f || num >= 0f || num2 >= 0f))
				{
					hostItems[((NetworkBehaviour)item).NetworkObjectId] = new ItemAssignment(item, num3, num4, num5, num, num2);
				}
				return;
			}
			extraData[((object)PoseKey).ToString()] = (JToken)(object)JObject.Parse(JsonUtility.ToJson((object)savedItemPose2));
			if (NetworkManager.Singleton.IsServer && ((NetworkBehaviour)item).NetworkObject.IsSpawned)
			{
				ItemAssignment itemAssignment = new ItemAssignment(item, savedItemPose2, furniture)
				{
					meshVariant = num3,
					materialVariant = num4,
					batteryCharge = num5,
					chemicalFuel = num,
					sprayPaintTank = num2
				};
				if ((Object)(object)cruiser != (Object)null)
				{
					itemAssignment.parentNetworkId = ((NetworkBehaviour)cruiser).NetworkObjectId;
				}
				hostItems[((NetworkBehaviour)item).NetworkObjectId] = itemAssignment;
				PlaceableObjectsSurfaceRegistry.BasedItems.Add(item);
			}
		}

		private static void ReadItem(GrabbableObject item, JObject extraData)
		{
			JToken val = extraData[((object)RemoveOnLoadKey).ToString()];
			if (val != null)
			{
				if (NetworkManager.Singleton.IsServer)
				{
					string reason = Extensions.Value<string>((IEnumerable<JToken>)val);
					if (reason == "deactivated")
					{
						item.deactivated = true;
					}
					else
					{
						item.itemUsedUp = true;
					}
					NetworkRefs.WhenReady<GrabbableObject>((MonoBehaviour)(object)StartOfRound.Instance, ((NetworkBehaviour)item).NetworkObjectId, (Action<GrabbableObject>)delegate(GrabbableObject removedItem)
					{
						ItemRemoved?.Invoke(removedItem, reason);
						((NetworkBehaviour)removedItem).NetworkObject.Despawn(true);
					}, 8f, 4);
				}
				return;
			}
			Mesh[] meshVariants = item.itemProperties.meshVariants;
			Material[] materialVariants = item.itemProperties.materialVariants;
			int item2 = -1;
			if (meshVariants != null)
			{
				item2 = ReadVariantIndex(item, extraData, MeshVariantKey, meshVariants.Length, "mesh");
			}
			int item3 = -1;
			if (materialVariants != null)
			{
				item3 = ReadVariantIndex(item, extraData, MaterialVariantKey, materialVariants.Length, "material");
			}
			float item4 = ReadAmount(item, extraData, BatteryChargeKey, "battery charge");
			float item5 = -1f;
			if (item is TetraChemicalItem)
			{
				item5 = ReadAmount(item, extraData, ChemicalFuelKey, "TZP fuel");
			}
			float item6 = -1f;
			if (item is SprayPaintItem)
			{
				item6 = ReadAmount(item, extraData, SprayPaintTankKey, "spray paint");
			}
			if (!NetworkManager.Singleton.IsServer)
			{
				return;
			}
			pendingItemValues[item] = (item2, item3, item4, item5, item6);
			JToken obj = extraData[((object)ItemIdKey).ToString()];
			string text = ((obj != null) ? obj.ToObject<string>() : null);
			if (!string.IsNullOrWhiteSpace(text))
			{
				loadedItems[text] = item;
			}
			JToken val2 = extraData[((object)PoseKey).ToString()];
			if (val2 == null)
			{
				return;
			}
			try
			{
				SavedItemPose savedItemPose = JsonUtility.FromJson<SavedItemPose>(((object)val2).ToString());
				bool flag = savedItemPose == null;
				bool flag2 = flag;
				if (!flag2)
				{
					ItemParentKind parentKind = savedItemPose.parentKind;
					bool flag3 = ((parentKind < ItemParentKind.Ship || parentKind > ItemParentKind.Cruiser) ? true : false);
					flag2 = flag3;
				}
				if (!flag2)
				{
					pending[item] = savedItemPose;
					if (savedItemPose.parentKind != ItemParentKind.BeltBag)
					{
						PlaceableObjectsSurfaceRegistry.BasedItems.Add(item);
					}
				}
			}
			catch (ArgumentException)
			{
				Log.Warn("Skipped unreadable saved position for " + item.itemProperties.itemName);
			}
		}

		private static int ReadVariantIndex(GrabbableObject item, JObject extraData, NamespacedKey key, int variantCount, string kind)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Invalid comparison between Unknown and I4
			JToken val = extraData[((object)key).ToString()];
			if (val == null)
			{
				return -1;
			}
			if ((int)val.Type != 6)
			{
				Log.Warn("Skipped invalid saved " + kind + " variant data for " + item.itemProperties.itemName);
				return -1;
			}
			int num = val.ToObject<int>();
			if (num < 0 || num >= variantCount)
			{
				Log.Warn($"Skipped invalid saved {kind} variant index {num} for {item.itemProperties.itemName}");
				return -1;
			}
			return num;
		}

		private static float ReadAmount(GrabbableObject item, JObject extraData, NamespacedKey key, string kind)
		{
			//IL_0017: 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_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Invalid comparison between Unknown and I4
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Invalid comparison between Unknown and I4
			JToken val = extraData[((object)key).ToString()];
			if (val == null)
			{
				return -1f;
			}
			JTokenType type = val.Type;
			if ((int)type != 7 && (int)type != 6)
			{
				Log.Warn("Skipped invalid saved " + kind + " data for " + item.itemProperties.itemName);
				return -1f;
			}
			float num = val.ToObject<float>();
			if (num < 0f || num > 1f)
			{
				Log.Warn($"Skipped invalid saved {kind} value {num} for {item.itemProperties.itemName}");
				return -1f;
			}
			return num;
		}

		private static void ItemsLoaded(StartOfRound round)
		{
			if (((NetworkBehaviour)round).IsServer)
			{
				_restoreCoroutine = ((MonoBehaviour)round).StartCoroutine(RestoreHost(round));
			}
		}

		private static void RoundStarted(StartOfRound round)
		{
			_unlockablesSynced = false;
			if (!((NetworkBehaviour)round).IsServer)
			{
				RequestAssignments();
			}
		}

		private static void GameSaved()
		{
			if (NetworkManager.Singleton.IsHost)
			{
				JObject[] source = (from item in ItemSaveDataHandler.AllShipItems
					where (Object)(object)item != (Object)null && collectedItems.ContainsKey(item)
					select collectedItems[item]).ToArray();
				int num = source.Count((JObject extraData) => extraData[((object)PoseKey).ToString()] != null);
				int num2 = source.Count((JObject extraData) => extraData[((object)MeshVariantKey).ToString()] != null);
				int num3 = source.Count((JObject extraData) => extraData[((object)MaterialVariantKey).ToString()] != null);
				int num4 = source.Count((JObject extraData) => extraData[((object)BatteryChargeKey).ToString()] != null);
				int num5 = source.Count((JObject extraData) => extraData[((object)ChemicalFuelKey).ToString()] != null);
				int num6 = source.Count((JObject extraData) => extraData[((object)SprayPaintTankKey).ToString()] != null);
				if (num > 0 || num2 > 0 || num3 > 0 || num4 > 0 || num5 > 0 || num6 > 0)
				{
					Log.Info($"Saved {num} item positions, {num2} mesh variants, {num3} material variants, {num4} battery charges, {num5} TZP fuel, and {num6} spray paint fuel");
				}
				collectedItems.Clear();
			}
		}

		private static IEnumerator RestoreHost(StartOfRound round)
		{
			yield return waitForSeconds05;
			while (Plugin.HasSelfSortingStorage && SelfSortingStorageCompat.Reloading)
			{
				yield return null;
			}
			ItemAssignmentList restored = new ItemAssignmentList();
			GrabbableObject key;
			(int, int, float, float, float) value;
			if (Plugin.HasSelfSortingStorage)
			{
				KeyValuePair<GrabbableObject, (int, int, float, float, float)>[] array = pendingItemValues.ToArray();
				foreach (KeyValuePair<GrabbableObject, (int, int, float, float, float)> keyValuePair in array)
				{
					keyValuePair.Deconstruct(out key, out value);
					GrabbableObject val = key;
					(int, int, float, float, float) tuple = value;
					if (!((Object)(object)val == (Object)null) && SelfSortingStorageCompat.OwnsPosition(val))
					{
						if (pending.Remove(val))
						{
							Log.Debug("Skipped saved position for " + val.itemProperties.itemName + " because SelfSortingStorage owns it");
						}
						Forget(val);
						pendingItemValues.Remove(val);
						if (tuple.Item1 >= 0 || tuple.Item2 >= 0 || !(tuple.Item3 < 0f) || !(tuple.Item4 < 0f) || !(tuple.Item5 < 0f))
						{
							ItemAssignment itemAssignment = new ItemAssignment(val, tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4, tuple.Item5);
							RestoreItemValues(val, itemAssignment, restored);
							hostItems[((NetworkBehaviour)val).NetworkObjectId] = itemAssignment;
							restored.variants.Add(itemAssignment);
						}
					}
				}
			}
			int missingFurnitureCount = 0;
			Dictionary<GrabbableObject, Animator[]> animators = pending.Keys.Where((GrabbableObject item) => (Object)(object)item != (Object)null).ToDictionary((GrabbableObject item) => item, (GrabbableObject item) => ((Component)item).GetComponentsInChildren<Animator>());
			Dictionary<GrabbableObject, float> animationFinishedAt = new Dictionary<GrabbableObject, float>();
			HashSet<GrabbableObject> processed = new HashSet<GrabbableObject>();
			float summaryAt = Time.realtimeSinceStartup + 10f;
			bool progressLogged = false;
			bool waiting = true;
			while (waiting)
			{
				waiting = false;
				foreach (KeyValuePair<GrabbableObject, SavedItemPose> item in pending)
				{
					item.Deconstruct(out key, out var value2);
					GrabbableObject val2 = key;
					SavedItemPose savedItemPose = value2;
					if ((Object)(object)val2 == (Object)null || processed.Contains(val2))
					{
						continue;
					}
					Animator[] array2 = animators[val2];
					if (array2.Length != 0)
					{
						if (val2.hasBeenHeld || val2.heldByPlayerOnServer || val2.isHeld || (Object)(object)val2.playerHeldBy != (Object)null || val2.isHeldByEnemy)
						{
							processed.Add(val2);
							Forget(val2);
							Log.Debug("Skipped saved position for animated item " + val2.itemProperties.itemName + " because it was picked up before restoration");
							continue;
						}
						if (!animationFinishedAt.TryGetValue(val2, out var value3))
						{
							if (!array2.All(delegate(Animator animator)
							{
								//IL_0025: Unknown result type (might be due to invalid IL or missing references)
								//IL_002a: Unknown result type (might be due to invalid IL or missing references)
								if (!((Object)(object)animator == (Object)null) && ((Behaviour)animator).isActiveAndEnabled && !Mathf.Approximately(animator.speed, 0f))
								{
									AnimatorStateInfo currentAnimatorStateInfo2 = animator.GetCurrentAnimatorStateInfo(0);
									return ((AnimatorStateInfo)(ref currentAnimatorStateInfo2)).normalizedTime >= 1f;
								}
								return true;
							}))
							{
								waiting = true;
								continue;
							}
							value3 = (animationFinishedAt[val2] = Time.realtimeSinceStartup + 0.1f);
						}
						if (Time.realtimeSinceStartup < value3)
						{
							waiting = true;
							continue;
						}
					}
					processed.Add(val2);
					ItemAssignment itemAssignment2 = null;
					if (savedItemPose.parentKind == ItemParentKind.BeltBag)
					{
						if (loadedItems.TryGetValue(savedItemPose.parentItemId, out GrabbableObject value4))
						{
							BeltBagItem val3 = (BeltBagItem)(object)((value4 is BeltBagItem) ? value4 : null);
							if (val3 != null && (Object)(object)val2 != (Object)(object)val3)
							{
								itemAssignment2 = new ItemAssignment(val2, savedItemPose)
								{
									parentNetworkId = ((NetworkBehaviour)val3).NetworkObjectId
								};
								if (!val3.objectsInBag.Contains(val2))
								{
									val2.heldByPlayerOnServer = true;
									val3.PutObjectInBagLocalClient(val2);
								}
								Utility.ItemRestored?.Invoke(val2, savedItemPose, null);
							}
						}
					}
					else if (!PlaceableObjectsSurfaceRegistry.IgnoredItems.Contains(val2))
					{
						PlaceableShipObject shipObject;
						Transform parent;
						bool unresolved;
						if (savedItemPose.parentKind == ItemParentKind.Ship)
						{
							if (Utility.PlaceShipPose(val2, savedItemPose, restored))
							{
								itemAssignment2 = new ItemAssignment(val2, savedItemPose);
							}
						}
						else if (savedItemPose.parentKind == ItemParentKind.Cruiser)
						{
							VehicleController attachedVehicle = round.attachedVehicle;
							if ((Object)(object)attachedVehicle != (Object)null && ((NetworkBehaviour)attachedVehicle).IsSpawned && ConfigManager.SaveCruiserItems.Value && (attachedVehicle.vehicleID != 0 || !Plugin.HasV55Cruiser))
							{
								if (Utility.PlaceFurniturePose(val2, ((Component)attachedVehicle).transform, savedItemPose, restored))
								{
									itemAssignment2 = new ItemAssignment(val2, savedItemPose)
									{
										parentNetworkId = ((NetworkBehaviour)attachedVehicle).NetworkObjectId
									};
								}
							}
							else if (Utility.PlaceShipPose(val2, savedItemPose, restored))
							{
								itemAssignment2 = new ItemAssignment(val2, savedItemPose);
								if (!ConfigManager.SaveCruiserItems.Value)
								{
									Log.Debug("Returned " + val2.itemProperties.itemName + " to ship because cruiser item saving is disabled");
								}
								else if ((Object)(object)attachedVehicle != (Object)null && attachedVehicle.vehicleID == 0 && Plugin.HasV55Cruiser)
								{
									Log.Debug("Returned " + val2.itemProperties.itemName + " to ship because v55 cruiser is installed");
								}
								else
								{
									Log.Warn("Returned " + val2.itemProperties.itemName + " to ship because cruiser could not be found");
								}
							}
						}
						else if (FurnitureRelationship.RestoreParent(round, val2, savedItemPose, out shipObject, out parent, out unresolved))
						{
							if (Utility.PlaceFurniturePose(val2, parent, savedItemPose, restored))
							{
								itemAssignment2 = new ItemAssignment(val2, savedItemPose);
								if (savedItemPose.parentKind == ItemParentKind.Furniture)
								{
									itemAssignment2.parentNetworkId = ((NetworkBehaviour)shipObject.parentObject).NetworkObjectId;
								}
							}
						}
						else if (unresolved && Utility.PlaceShipPose(val2, savedItemPose, restored))
						{
							itemAssignment2 = new ItemAssignment(val2, savedItemPose);
							missingFurnitureCount++;
						}
					}
					if (itemAssignment2 == null)
					{
						Forget(val2);
						continue;
					}
					if (pendingItemValues.TryGetValue(val2, out (int, int, float, float, float) value5))
					{
						(itemAssignment2.meshVariant, itemAssignment2.materialVariant, itemAssignment2.batteryCharge, itemAssignment2.chemicalFuel, itemAssignment2.sprayPaintTank) = value5;
						pendingItemValues.Remove(val2);
					}
					RestoreItemValues(val2, itemAssignment2, restored);
					hostItems[((NetworkBehaviour)val2).NetworkObjectId] = itemAssignment2;
					if (savedItemPose.parentKind != ItemParentKind.BeltBag)
					{
						PlaceableObjectsSurfaceRegistry.BasedItems.Add(val2);
					}
					restored.items.Add(itemAssignment2);
				}
				if (!waiting)
				{
					continue;
				}
				if (!progressLogged && Time.realtimeSinceStartup >= summaryAt)
				{
					int num2 = pending.Keys.Count((GrabbableObject item) => (Object)(object)item != (Object)null && !processed.Contains(item));
					Log.Info($"Restored {restored.items.Count} item positions, {restored.meshVariantCount} mesh variants, {restored.materialVariantCount} material variants, {restored.batteryCount} battery charges, {restored.chemicalCount} TZP fuel and {restored.sprayPaintCount} spray paint/weed killer fuel. Awaiting animations on {num2} items");
					if (restored.colliderCounts.Count > 0)
					{
						Log.Debug("Restored " + string.Join(", ", restored.colliderCounts.Select<KeyValuePair<string, int>, string>((KeyValuePair<string, int> entry) => $"{entry.Value} item positions inside {entry.Key}")));
					}
					if (restored.returnedToShipCount > 0)
					{
						Log.Debug($"Returned {restored.returnedToShipCount} items to the middle of the ship");
					}
					foreach (GrabbableObject key2 in pending.Keys)
					{
						if ((Object)(object)key2 == (Object)null || processed.Contains(key2))
						{
							continue;
						}
						if (animationFinishedAt.TryGetValue(key2, out var value6) && Time.realtimeSinceStartup < value6)
						{
							Log.Debug("Awaiting animated item " + key2.itemProperties.itemName + " to finish its 0.1 second settling delay");
							continue;
						}
						bool flag = false;
						Animator[] array3 = animators[key2];
						foreach (Animator val4 in array3)
						{
							if (!((Object)(object)val4 == (Object)null) && ((Behaviour)val4).isActiveAndEnabled)
							{
								AnimatorStateInfo currentAnimatorStateInfo = val4.GetCurrentAnimatorStateInfo(0);
								if (!Mathf.Approximately(val4.speed, 0f) && !(((AnimatorStateInfo)(ref currentAnimatorStateInfo)).normalizedTime >= 1f))
								{
									Log.Debug($"Awaiting animated item {key2.itemProperties.itemName} child {((Object)val4).name} at normalised time {((AnimatorStateInfo)(ref currentAnimatorStateInfo)).normalizedTime:G9} with speed {val4.speed:G9}");
									flag = true;
								}
							}
						}
						if (!flag)
						{
							Log.Debug("Awaiting animated item " + key2.itemProperties.itemName + " for the next restoration check");
						}
					}
					progressLogged = true;
				}
				yield return null;
			}
			foreach (KeyValuePair<GrabbableObject, (int, int, float, float, float)> pendingItemValue in pendingItemValues)
			{
				pendingItemValue.Deconstruct(out key, out value);
				GrabbableObject val5 = key;
				(int, int, float, float, float) tuple3 = value;
				if (!((Object)(object)val5 == (Object)null) && (tuple3.Item1 >= 0 || tuple3.Item2 >= 0 || !(tuple3.Item3 < 0f) || !(tuple3.Item4 < 0f) || !(tuple3.Item5 < 0f)))
				{
					ItemAssignment itemAssignment3 = new ItemAssignment(val5, tuple3.Item1, tuple3.Item2, tuple3.Item3, tuple3.Item4, tuple3.Item5);
					RestoreItemValues(val5, itemAssignment3, restored);
					hostItems[((NetworkBehaviour)val5).NetworkObjectId] = itemAssignment3;
					restored.variants.Add(itemAssignment3);
				}
			}
			if (missingFurnitureCount > 0)
			{
				Log.Warn($"Moved {missingFurnitureCount} items to the ship because their saved furniture or parent could not be found");
			}
			if (restored.items.Count > 0 || restored.variants.Count > 0)
			{
				Log.Info($"Restored {restored.items.Count} item positions, {restored.meshVariantCount} mesh variants, {restored.materialVariantCount} material variants, {restored.batteryCount} battery charges, {restored.chemicalCount} TZP fuel and {restored.sprayPaintCount} spray paint fuel");
				if (restored.colliderCounts.Count > 0)
				{
					Log.Debug("Restored " + string.Join(", ", restored.colliderCounts.Select<KeyValuePair<string, int>, string>((KeyValuePair<string, int> entry) => $"{entry.Value} item positions inside {entry.Key}")));
				}
				if (restored.returnedToShipCount > 0)
				{
					Log.Debug($"Returned {restored.returnedToShipCount} items to the middle of the ship");
				}
				if (NetworkManager.Singleton.IsListening)
				{
					if (HatNet.SendToClients(AssignmentKey, JsonUtility.ToJson((object)restored)))
					{
						ItemsSynced?.Invoke(restored, null, arg3: true);
					}
					else
					{
						Log.Error("Restored item data could not be sent to clients");
					}
				}
			}
			pending.Clear();
			pendingItemValues.Clear();
			loadedItems.Clear();
			_restoreCoroutine = null;
		}

		private static SavedItemPose? PoseFor(GrabbableObject item, out FurnitureRelationship? furniture, out VehicleController? cruiser)
		{
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: 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_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			//IL_020a: 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)
			//IL_0125: 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_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: 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_00fe: 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)
			//IL_0312: Unknown result type (might be due to invalid IL or missing references)
			//IL_0317: Unknown result type (might be due to invalid IL or missing references)
			//IL_031c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0329: Unknown result type (might be due to invalid IL or missing references)
			//IL_032e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0339: Unknown result type (might be due to invalid IL or missing references)
			//IL_033e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0343: Unknown result type (might be due to invalid IL or missing references)
			furniture = null;
			cruiser = null;
			if (PlaceableObjectsSurfaceRegistry.IgnoredItems.Contains(item))
			{
				ItemPositionSkipped?.Invoke(item, "it was ignored through the API");
				return null;
			}
			if (Plugin.HasSelfSortingStorage && SelfSortingStorageCompat.OwnsPosition(item))
			{
				ItemPositionSkipped?.Invoke(item, "SelfSortingStorage owns it");
				return null;
			}
			bool flag = item.heldByPlayerOnServer || (Object)(object)item.playerHeldBy != (Object)null || (item != null && item.isHeld && !item.isHeldByEnemy);
			Vector3 val = default(Vector3);
			Quaternion val2 = default(Quaternion);
			((Component)item).transform.GetPositionAndRotation(ref val, ref val2);
			if (flag)
			{
				cruiser = StartOfRound.Instance.attachedVehicle;
				Vector3 val3 = default(Vector3);
				if ((Object)(object)cruiser != (Object)null && ((NetworkBehaviour)cruiser).IsSpawned && ConfigManager.SaveCruiserItems.Value && (cruiser.vehicleID != 0 || !Plugin.HasV55Cruiser) && (Object)(object)item.GetPhysicsRegionOfDroppedObject(item.playerHeldBy, ref val3, true) == (Object)(object)((NetworkBehaviour)cruiser).NetworkObject)
				{
					val = cruiser.physicsRegion.physicsTransform.TransformPoint(val3);
				}
				else
				{
					cruiser = null;
					val = item.GetItemFloorPosition(default(Vector3));
				}
				Vector3 restingRotation = item.itemProperties.restingRotation;
				float num = ((Component)item).transform.eulerAngles.y;
				if ((Object)(object)item.playerHeldBy != (Object)null)
				{
					num = (float)((int)((Component)item.playerHeldBy).transform.localEulerAngles.y + item.itemProperties.floorYOffset) + 90f;
				}
				val2 = Quaternion.Euler(restingRotation.x, num, restingRotation.z);
				Log.Debug("Calculated floor position for held item " + item.itemProperties.itemName);
			}
			SavedItemPose savedItemPose = new SavedItemPose
			{
				parentKind = ItemParentKind.Ship,
				worldPosition = val,
				worldRotation = val2
			};
			if (!flag)
			{
				cruiser = Utility.CruiserAt(val, ((Component)item).transform);
			}
			if ((Object)(object)cruiser != (Object)null)
			{
				savedItemPose.parentKind = ItemParentKind.Cruiser;
				savedItemPose.localPosition = ((Component)cruiser).transform.InverseTransformPoint(val);
				savedItemPose.localRotation = Quaternion.Inverse(((Component)cruiser).transform.rotation) * val2;
				return savedItemPose;
			}
			if (item is ClipboardItem || (item is PhysicsProp && item.itemProperties.itemName == "Sticky note"))
			{
				return savedItemPose;
			}
			bool surfaceFound;
			FurnitureRelationship furnitureRelationship = FurnitureRelationship.FromItem(item, out surfaceFound);
			if (!surfaceFound)
			{
				return savedItemPose;
			}
			if (furnitureRelationship == null)
			{
				ItemPositionSkipped?.Invoke(item, "its furniture or parent could not be found");
				return null;
			}
			if (ConfigManager.ExcludedUnlockables.Contains(furnitureRelationship.FurnitureKey))
			{
				ItemPositionSkipped?.Invoke(item, $"its furniture {furnitureRelationship.FurnitureKey} is excluded in the config");
				return null;
			}
			if (PlaceableObjectsSurfaceRegistry.TryGet(furnitureRelationship.Surface, out var registration) && !registration.AllowAutomaticSaving)
			{
				ItemPositionSkipped?.Invoke(item, registration.OwnerGuid + " disabled automatic saving for its surface through the API");
				return null;
			}
			furniture = furnitureRelationship;
			savedItemPose.parentKind = ItemParentKind.Furniture;
			savedItemPose.furnitureKey = ((object)furnitureRelationship.FurnitureKey).ToString();
			savedItemPose.furnitureParentPath = furnitureRelationship.ParentPath;
			savedItemPose.localPosition = furnitureRelationship.ItemParent.InverseTransformPoint(((Component)item).transform.position);
			savedItemPose.localRotation = Quaternion.Inverse(furnitureRelationship.ItemParent.rotation) * ((Component)item).transform.rotation;
			return savedItemPose;
		}

		private static void Forget(GrabbableObject item)
		{
			if (NetworkManager.Singleton.IsServer)
			{
				hostItems.Remove(((NetworkBehaviour)item).NetworkObjectId);
				PlaceableObjectsSurfaceRegistry.BasedItems.Remove(item);
			}
		}

		private static void UnlockablesSynced(StartOfRound round)
		{
			if (!((NetworkBehaviour)round).IsServer)
			{
				_unlockablesSynced = true;
				RequestAssignments();
			}
		}

		private static void RequestAssignments()
		{
			if (NetworkManager.Singleton.IsListening && !HatNet.SendToServer(RequestKey, string.Empty))
			{
				Log.Error("Current item data could not be requested from the server");
			}
		}

		private static void AssignmentsRequested(ulong clientId, string data)
		{
			StartOfRound instance = StartOfRound.Instance;
			((MonoBehaviour)instance).StartCoroutine(SendAssignments(instance, clientId));
		}

		private static IEnumerator SendAssignments(StartOfRound round, ulong clientId)
		{
			while (Plugin.HasSelfSortingStorage && SelfSortingStorageCompat.Reloading)
			{
				yield return null;
			}
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)round == (Object)null || !((NetworkBehaviour)round).IsSpawned || !singleton.IsListening || !singleton.ConnectedClients.ContainsKey(clientId))
			{
				yield break;
			}
			hostItems.Clear();
			GrabbableObject[] array = (from networkObject in singleton.SpawnManager.SpawnedObjects.Values
				select ((Component)networkObject).GetComponent<GrabbableObject>() into item
				where (Object)(object)item != (Object)null && ((Component)item).gameObject.activeInHierarchy
				select item).ToArray();
			GrabbableObject[] array2 = array;
			foreach (GrabbableObject val in array2)
			{
				if (ItemExtensions.GetDawnInfo(val.itemProperties) != null)
				{
					SaveItem(val, new JObject(), array);
				}
			}
			ItemAssignmentList itemAssignmentList = new ItemAssignmentList
			{
				items = hostItems.Values.Where((ItemAssignment assignment) => !assignment.valuesOnly).ToList(),
				variants = hostItems.Values.Where((ItemAssignment assignment) => assignment.valuesOnly).ToList()
			};
			int num2 = hostItems.Values.Count((ItemAssignment assignment) => assignment.meshVariant >= 0);
			int num3 = hostItems.Values.Count((ItemAssignment assignment) => assignment.materialVariant >= 0);
			int num4 = hostItems.Values.Count((ItemAssignment assignment) => assignment.batteryCharge >= 0f);
			int num5 = hostItems.Values.Count((ItemAssignment assignment) => assignment.chemicalFuel >= 0f);
			int num6 = hostItems.Values.Count((ItemAssignment assignment) => assignment.sprayPaintTank >= 0f);
			if (HatNet.SendToClient(AssignmentKey, clientId, JsonUtility.ToJson((object)itemAssignmentList)))
			{
				Log.Debug($"Sent {itemAssignmentList.items.Count} item positions, {num2} mesh variants, {num3} material variants, {num4} battery charges, {num5} TZP fuel, and {num6} spray paint fuel to client {clientId}");
				ItemsSynced?.Invoke(itemAssignmentList, clientId, arg3: true);
			}
			else
			{
				Log.Error($"Current item data could not be sent to client {clientId}");
			}
		}

		private static void AssignmentsReceived(ulong clientId, string data)
		{
			if (NetworkManager.Singleton.IsServer || clientId != 0L)
			{
				return;
			}
			try
			{
				ItemAssignmentList itemAssignmentList = JsonUtility.FromJson<ItemAssignmentList>(data);
				StartOfRound instance = StartOfRound.Instance;
				if (itemAssignmentList?.items == null)
				{
					return;
				}
				ItemsSynced?.Invoke(itemAssignmentList, clientId, arg3: false);
				foreach (ItemAssignment item in itemAssignmentList.items)
				{
					if (item.pose.parentKind != ItemParentKind.BeltBag)
					{
						NetworkRefs.WhenReady<GrabbableObject>((MonoBehaviour)(object)instance, item.itemNetworkId, (Action<GrabbableObject>)delegate(GrabbableObject item)
						{
							PlaceableObjectsSurfaceRegistry.BasedItems.Add(item);
						}, 8f, 0);
					}
				}
				if (_unlockablesSynced)
				{
					PlaceClientItems(instance, itemAssignmentList);
				}
			}
			catch (ArgumentException)
			{
				Log.Error("Item data received from the server could not be read");
			}
		}

		private static void PlaceClientItems(StartOfRound round, ItemAssignmentList assignments)
		{
			if (assignments.items.Count == 0 && assignments.variants.Count == 0)
			{
				return;
			}
			assignments.colliderCounts = new Dictionary<string, int>();
			Dictionary<ItemAssignment, string> unplaced = new Dictionary<ItemAssignment, string>();
			foreach (ItemAssignment assignment in assignments.variants)
			{
				NetworkRefs.WhenReady<GrabbableObject>((MonoBehaviour)(object)round, assignment.itemNetworkId, (Action<GrabbableObject>)delegate(GrabbableObject item)
				{
					RestoreItemValues(item, assignment, assignments);
				}, 8f, 4);
			}
			foreach (ItemAssignment assignment2 in assignments.items)
			{
				unplaced[assignment2] = "the item did not become ready for placement";
				NetworkRefs.WhenReady<GrabbableObject>((MonoBehaviour)(object)round, assignment2.itemNetworkId, (Action<GrabbableObject>)delegate(GrabbableObject item)
				{
					RestoreItemValues(item, assignment2, assignments);
				}, 8f, 4);
				if (assignment2.pose.parentKind == ItemParentKind.BeltBag)
				{
					unplaced[assignment2] = "its belt bag could not be resolved";
					NetworkRefs.WhenReady<BeltBagItem>((MonoBehaviour)(object)round, assignment2.parentNetworkId, (Action<BeltBagItem>)delegate(BeltBagItem beltBag)
					{
						unplaced[assignment2] = "the item did not become ready for placement";
						NetworkRefs.WhenReady<GrabbableObject>((MonoBehaviour)(object)round, assignment2.itemNetworkId, (Action<GrabbableObject>)delegate(GrabbableObject item)
						{
							if (Plugin.HasSelfSortingStorage && SelfSortingStorageCompat.OwnsPosition(item))
							{
								PlaceableObjectsSurfaceRegistry.BasedItems.Remove(item);
								unplaced[assignment2] = "SelfSortingStorage owns it";
							}
							else if (item.isHeld || item.heldByPlayerOnServer || (Object)(object)item.playerHeldBy != (Object)null || item.isHeldByEnemy)
							{
								unplaced[assignment2] = "it was being held";
							}
							else
							{
								if (!beltBag.objectsInBag.Contains(item))
								{
									beltBag.PutObjectInBagLocalClient(item);
								}
								Utility.ItemRestored?.Invoke(item, assignment2.pose, null);
								assignments.placedCount++;
								unplaced.Remove(assignment2);
							}
						}, 8f, 4);
					}, 8f, 0);
					continue;
				}
				if (assignment2.pose.parentKind == ItemParentKind.Ship)
				{
					NetworkRefs.WhenReady<GrabbableObject>((MonoBehaviour)(object)round, assignment2.itemNetworkId, (Action<GrabbableObject>)delegate(GrabbableObject item)
					{
						if (Plugin.HasSelfSortingStorage && SelfSortingStorageCompat.OwnsPosition(item))
						{
							PlaceableObjectsSurfaceRegistry.BasedItems.Remove(item);
							unplaced[assignment2] = "SelfSortingStorage owns it";
						}
						else if (item.isHeld || item.heldByPlayerOnServer || (Object)(object)item.playerHeldBy != (Object)null || item.isHeldByEnemy)
						{
							unplaced[assignment2] = "it was being held";
						}
						else if (Utility.PlaceShipPose(item, assignment2.pose, assignments))
						{
							assignments.placedCount++;
							unplaced.Remove(assignment2);
						}
						else
						{
							unplaced[assignment2] = "the item was no longer available when placement ran";
						}
					}, 8f, 4);
					continue;
				}
				if (assignment2.pose.parentKind == ItemParentKind.Cruiser)
				{
					unplaced[assignment2] = "its cruiser could not be resolved";
				}
				else
				{
					unplaced[assignment2] = "its furniture could not be resolved";
				}
				NetworkRefs.WhenReady((MonoBehaviour)(object)round, assignment2.parentNetworkId, (Action<NetworkObject>)delegate(NetworkObject root)
				{
					Transform parent;
					if (assignment2.pose.parentKind == ItemParentKind.Cruiser)
					{
						parent = ((Component)root).transform;
					}
					else
					{
						parent = Utility.FurnitureParentFromPath(((Component)root).transform, assignment2.pose.furnitureParentPath);
					}
					if ((Object)(object)parent == (Object)null)
					{
						unplaced[assignment2] = "its saved parent could not be found on " + ((Object)root).name;
					}
					else
					{
						unplaced[assignment2] = "the item did not become ready for placement";
						NetworkRefs.WhenReady<GrabbableObject>((MonoBehaviour)(object)round, assignment2.itemNetworkId, (Action<GrabbableObject>)delegate(GrabbableObject item)
						{
							if (Plugin.HasSelfSortingStorage && SelfSortingStorageCompat.OwnsPosition(item))
							{
								PlaceableObjectsSurfaceRegistry.BasedItems.Remove(item);
								unplaced[assignment2] = "SelfSortingStorage owns it";
							}
							else if (item.isHeld || item.heldByPlayerOnServer || (Object)(object)item.playerHeldBy != (Object)null || item.isHeldByEnemy)
							{
								unplaced[assignment2] = "it was being held";
							}
							else if (Utility.PlaceFurniturePose(item, parent, assignment2.pose, assignments))
							{
								assignments.placedCount++;
								unplaced.Remove(assignment2);
							}
							else
							{
								unplaced[assignment2] = "the item was no longer available when placement ran";
							}
						}, 8f, 4);
					}
				}, 8f, 0);
			}
			((MonoBehaviour)round).StartCoroutine(ClientPlacementSummary(round, assignments, unplaced));
		}

		private static IEnumerator ClientPlacementSummary(StartOfRound round, ItemAssignmentList assignments, Dictionary<ItemAssignment, string> unplaced)
		{
			yield return waitForSecondsRealtime1625;
			if ((Object)(object)round == (Object)null || !((NetworkBehaviour)round).IsSpawned)
			{
				yield break;
			}
			Log.Info($"Client restored {assignments.placedCount} of {assignments.items.Count} item positions, {assignments.meshVariantCount} mesh variants, {assignments.materialVariantCount} material variants, {assignments.batteryCount} battery charges, {assignments.chemicalCount} TZP fuel and {assignments.sprayPaintCount} spray paint fuel from server");
			if (assignments.colliderCounts.Count > 0)
			{
				Log.Debug("Client restored " + string.Join(", ", assignments.colliderCounts.Select<KeyValuePair<string, int>, string>((KeyValuePair<string, int> entry) => $"{entry.Value} item positions inside {entry.Key}")));
			}
			if (assignments.returnedToShipCount > 0)
			{
				Log.Debug($"Client returned {assignments.returnedToShipCount} items to the middle of the ship");
			}
			foreach (var (itemAssignment2, text2) in unplaced)
			{
				Log.Debug("Client has not restored the saved position for " + itemAssignment2.itemName + " because " + text2);
			}
		}

		private static void RestoreItemValues(GrabbableObject item, ItemAssignment assignment, ItemAssignmentList assignments)
		{
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Expected O, but got Unknown
			Mesh[] meshVariants = item.itemProperties.meshVariants;
			Material[] materialVariants = item.itemProperties.materialVariants;
			if (assignment.meshVariant >= 0)
			{
				MeshFilter val = default(MeshFilter);
				if (meshVariants != null && assignment.meshVariant < meshVariants.Length && ((Component)item).TryGetComponent<MeshFilter>(ref val))
				{
					val.sharedMesh = meshVariants[assignment.meshVariant];
					assignments.meshVariantCount++;
					ItemValueRestored?.Invoke(item, "mesh variant", assignment.meshVariant);
				}
				else
				{
					Log.Warn($"Skipped unavailable mesh variant {assignment.meshVariant} for {item.itemProperties.itemName}");
				}
			}
			if (assignment.materialVariant >= 0)
			{
				MeshRenderer val2 = default(MeshRenderer);
				if (materialVariants != null && assignment.materialVariant < materialVariants.Length && ((Component)item).TryGetComponent<MeshRenderer>(ref val2))
				{
					((Renderer)val2).sharedMaterial = materialVariants[assignment.materialVariant];
					assignments.materialVariantCount++;
					ItemValueRestored?.Invoke(item, "material variant", assignment.materialVariant);
				}
				else
				{
					Log.Warn($"Skipped unavailable material variant {assignment.materialVariant} for {item.itemProperties.itemName}");
				}
			}
			if (assignment.batteryCharge >= 0f)
			{
				item.insertedBattery = new Battery(assignment.batteryCharge <= 0f, assignment.batteryCharge);
				item.ChargeBatteries();
				assignments.batteryCount++;
				ItemValueRestored?.Invoke(item, "battery charge", assignment.batteryCharge);
			}
			if (assignment.chemicalFuel >= 0f)
			{
				TetraChemicalItem val3 = (TetraChemicalItem)(object)((item is TetraChemicalItem) ? item : null);
				if (val3 != null)
				{
					val3.fuel = assignment.chemicalFuel;
					((GrabbableObject)val3).itemUsedUp = assignment.chemicalFuel <= 0f;
					assignments.chemicalCount++;
					ItemValueRestored?.Invoke(item, "TZP fuel", assignment.chemicalFuel);
				}
				else
				{
					Log.Warn("Skipped saved TZP fuel for incompatible item " + item.itemProperties.itemName);
				}
			}
			if (assignment.sprayPaintTank >= 0f)
			{
				SprayPaintItem val4 = (SprayPaintItem)(object)((item is SprayPaintItem) ? item : null);
				if (val4 != null)
				{
					val4.sprayCanTank = assignment.sprayPaintTank;
					assignments.sprayPaintCount++;
					ItemValueRestored?.Invoke(item, "spray paint/weed killer fuel", assignment.sprayPaintTank);
				}
				else
				{
					Log.Warn("Skipped saved spray paint fuel for incompatible item " + item.itemProperties.itemName);
				}
			}
		}

		private static void RoundCleared(StartOfRound round)
		{
			if (_restoreCoroutine != null)
			{
				((MonoBehaviour)round).StopCoroutine(_restoreCoroutine);
				_restoreCoroutine = null;
			}
			_unlockablesSynced = false;
			pending.Clear();
			pendingItemValues.Clear();
			loadedItems.Clear();
			hostItems.Clear();
			PlaceableObjectsSurfaceRegistry.IgnoredItems.Clear();
			PlaceableObjectsSurfaceRegistry.BasedItems.Clear();
			collectedItems.Clear();
		}
	}
	internal enum ItemParentKind
	{
		Ship,
		Furniture,
		BeltBag,
		Cruiser
	}
	[Serializable]
	internal class SavedItemPose
	{
		public ItemParentKind parentKind;

		public Vector3 worldPosition;

		public Vector3 localPosition;

		public Quaternion worldRotation;

		public Quaternion localRotation;

		public string furnitureKey = string.Empty;

		public string furnitureParentPath = string.Empty;

		public string parentItemId = string.Empty;
	}
	[Serializable]
	internal class ItemAssignment
	{
		public string itemName;

		public ulong itemNetworkId;

		public ulong parentNetworkId;

		public int meshVariant = -1;

		public int materialVariant = -1;

		public float batteryCharge = -1f;

		public float chemicalFuel = -1f;

		public float sprayPaintTank = -1f;

		public SavedItemPose pose;

		internal bool valuesOnly;

		internal ItemAssignment(GrabbableObject item, SavedItemPose pose, FurnitureRelationship? furniture = null)
		{
			itemName = item.itemProperties.itemName;
			itemNetworkId = ((NetworkBehaviour)item).NetworkObjectId;
			if (furniture != null)
			{
				parentNetworkId = ((NetworkBehaviour)furniture.ShipObject.parentObject).NetworkObjectId;
			}
			this.pose = pose;
		}

		internal ItemAssignment(GrabbableObject item, int meshVariant, int materialVariant, float batteryCharge, float chemicalFuel, float sprayPaintTank)
		{
			itemName = item.itemProperties.itemName;
			itemNetworkId = ((NetworkBehaviour)item).NetworkObjectId;
			this.meshVariant = meshVariant;
			this.materialVariant = materialVariant;
			this.batteryCharge = batteryCharge;
			this.chemicalFuel = chemicalFuel;
			this.sprayPaintTank = sprayPaintTank;
			valuesOnly = true;
		}
	}
	[Serializable]
	internal class ItemAssignmentList
	{
		public List<ItemAssignment> items = new List<ItemAssignment>();

		public List<ItemAssignment> variants = new List<ItemAssignment>();

		internal Dictionary<string, int> colliderCounts = new Dictionary<string, int>();

		internal int placedCount;

		internal int meshVariantCount;

		internal int materialVariantCount;

		internal int batteryCount;

		internal int chemicalCount;

		internal int sprayPaintCount;

		internal int returnedToShipCount;
	}
}
namespace BasedItemSaving.Compat
{
	internal static class PocketRoomsLibCompat
	{
		[MethodImpl(MethodImplOptions.NoInlining)]
		internal static Collider? SavingColliderAt(Vector3 position)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			foreach (Collider roomBound in MelShipBoundsCheck.roomBounds)
			{
				if ((Object)(object)roomBound != (Object)null && roomBound.enabled && ((Component)roomBound).gameObject.activeInHierarchy)
				{
					Bounds bounds = roomBound.bounds;
					if (((Bounds)(ref bounds)).Contains(position))
					{
						return roomBound;
					}
				}
			}
			return null;
		}
	}
	internal static class SelfSortingStorageCompat
	{
		private static Component _cupboard = null;

		private static readonly HashSet<IEnumerator> reloads = new HashSet<IEnumerator>();

		internal static bool Reloading
		{
			[MethodImpl(MethodImplOptions.NoInlining)]
			get
			{
				if ((Object)(object)_cupboard != (Object)null)
				{
					return reloads.Count > 0;
				}
				return false;
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		internal static bool OwnsPosition(GrabbableObject item)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			if ((Object)(object)_cupboard == (Object)null || item.isHeld || item.heldByPlayerOnServer || (Object)(object)item.playerHeldBy != (Object)null || item.isHeldByEnemy)
			{
				return false;
			}
			SmartCupboard val = (SmartCupboard)_cupboard;
			if (val.placedItems.ContainsValue(item))
			{
				return true;
			}
			Transform parent = ((Component)item).transform.parent;
			Transform[] placePositions = val.placePositions;
			foreach (Transform val2 in placePositions)
			{
				if ((Object)(object)val2 != (Object)null && (Object)(object)parent == (Object)(object)val2)
				{
					return true;
				}
			}
			return false;
		}

		[HarmonyPatch(typeof(SmartCupboard), "OnNetworkSpawn")]
		[HarmonyPostfix]
		private static void OnNetworkSpawnPostfix(SmartCupboard __instance)
		{
			_cupboard = (Component)(object)__instance;
			reloads.Clear();
		}

		[HarmonyPatch(typeof(SmartCupboard), "OnNetworkDespawn")]
		[HarmonyPostfix]
		private static void OnNetworkDespawnPostfix(SmartCupboard __instance)
		{
			if (!((Object)(object)_cupboard != (Object)(object)__instance))
			{
				_cupboard = null;
				reloads.Clear();
			}
		}

		[HarmonyPatch(typeof(SmartCupboard), "ReloadPlacedItems")]
		[HarmonyPostfix]
		private static IEnumerator ReloadPlacedItemsPostfix(IEnumerator values)
		{
			reloads.Add(values);
			try
			{
				while (values.MoveNext())
				{
					yield return values.Current;
				}
			}
			finally
			{
				reloads.Remove(values);
				if (values is IDisposable disposable)
				{
					disposable.Dispose();
				}
			}
		}
	}
}
namespace BasedItemSaving.API
{
	public readonly struct PlaceableObjectsSurfaceRegistrationAPI
	{
		public string OwnerGuid { get; }

		public Type SurfaceType { get; }

		public bool AllowAutomaticSaving { get; }

		public PlaceableObjectsSurfaceRegistrationAPI(string ownerGuid, Type surfaceType, bool allowAutomaticSaving)
		{
			OwnerGuid = ownerGuid;
			SurfaceType = surfaceType;
			AllowAutomaticSaving = allowAutomaticSaving;
		}
	}
	public static class PlaceableObjectsSurfaceRegistry
	{
		private static readonly List<PlaceableObjectsSurfaceRegistrationAPI> registrations = new List<PlaceableObjectsSurfaceRegistrationAPI>();

		internal static readonly HashSet<GrabbableObject> IgnoredItems = new HashSet<GrabbableObject>();

		internal static readonly HashSet<GrabbableObject> BasedItems = new HashSet<GrabbableObject>();

		public static void Register<TSurface>(string ownerGuid, bool allowAutomaticSaving) where TSurface : PlaceableObjectsSurface
		{
			Type surfaceType = typeof(TSurface);
			if (!registrations.Any((PlaceableObjectsSurfaceRegistrationAPI entry) => entry.OwnerGuid == ownerGuid && entry.SurfaceType == surfaceType))
			{
				registrations.Add(new PlaceableObjectsSurfaceRegistrationAPI(ownerGuid, surfaceType, allowAutomaticSaving));
			}
		}

		public static bool TryGet(PlaceableObjectsSurface surface, out PlaceableObjectsSurfaceRegistrationAPI registration)
		{
			registration = registrations.FirstOrDefault((PlaceableObjectsSurfaceRegistrationAPI entry) => entry.SurfaceType.IsInstanceOfType(surface));
			return registration.SurfaceType != null;
		}

		public static void Ignore(GrabbableObject item)
		{
			IgnoredItems.Add(item);
		}

		public static void Allow(GrabbableObject item)
		{
			IgnoredItems.Remove(item);
		}

		public static bool IsIgnored(GrabbableObject item)
		{
			if ((Object)(object)item != (Object)null)
			{
				if (!IgnoredItems.Contains(item))
				{
					return BasedItems.Contains(item);
				}
				return true;
			}
			return false;
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}