Decompiled source of BasedItemSaving v0.1.4

BasedItemSaving.dll

Decompiled 3 days 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.Saving;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Dawn;
using Dawn.Internal;
using HatLib;
using HatLib.Tools;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json.Linq;
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("0.1.4.0")]
[assembly: AssemblyInformationalVersion("0.1.4")]
[assembly: AssemblyProduct("BasedItemSaving")]
[assembly: AssemblyTitle("BasedItemSaving")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.4.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
	{
		private static readonly HashSet<NamespacedKey<DawnUnlockableItemInfo>> excludedUnlockables = new HashSet<NamespacedKey<DawnUnlockableItemInfo>>();

		internal static void Bind(ConfigFile config)
		{
			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);
			};
		}

		internal static bool IsExcluded(NamespacedKey<DawnUnlockableItemInfo> key)
		{
			return excludedUnlockables.Contains(key);
		}

		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.Error("Excluded unlockable key " + text2 + " could not be read");
				}
			}
		}
	}
	internal static class Log
	{
		internal static void Debug(string message)
		{
			Write((LogLevel)32, message);
		}

		internal static void Info(string message)
		{
			Write((LogLevel)16, message);
		}

		internal static void Warn(string message)
		{
			Write((LogLevel)4, message);
		}

		internal static void Error(string message)
		{
			Write((LogLevel)2, message);
		}

		internal static void Fatal(string message)
		{
			Write((LogLevel)1, message);
		}

		private static void Write(LogLevel logLevel, string message)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			Plugin.mls.Log(logLevel, (object)message);
		}
	}
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("MrHat.BasedItemSaving", "BasedItemSaving", "0.1.4")]
	internal class Plugin : BaseUnityPlugin
	{
		public const string modGUID = "MrHat.BasedItemSaving";

		private const string modName = "BasedItemSaving";

		private const string modVersion = "0.1.4";

		internal static ManualLogSource mls;

		private void Awake()
		{
			mls = Logger.CreateLogSource("MrHat.BasedItemSaving");
			ShipItemSaving.Init();
			mls.LogInfo((object)"BasedItemSaving 0.1.4 loaded");
		}
	}
	internal static class Utility
	{
		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, Vector3 position, Quaternion rotation)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: 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_0047: 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_0051: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)item == (Object)null || !GrabbablePlacement.Place(item, parent, parent.TransformPoint(position), parent.rotation * rotation))
			{
				return false;
			}
			((Component)item).transform.SetLocalPositionAndRotation(position, rotation);
			item.targetFloorPosition = position;
			item.startFallingPosition = position + Vector3.up * 0.0001f;
			return true;
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "BasedItemSaving";

		public const string PLUGIN_NAME = "BasedItemSaving";

		public const string PLUGIN_VERSION = "0.1.4";
	}
}
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_0022: Unknown result type (might be due to invalid IL or missing references)
			surfaceFound = false;
			Transform parent = ((Component)item).transform.parent;
			PlaceableObjectsSurface val = null;
			if ((Object)(object)parent != (Object)null)
			{
				val = SurfaceAt(parent, item, ((Component)item).transform.position, Object.FindObjectsByType<PlaceableObjectsSurface>((FindObjectsSortMode)0), out surfaceFound);
			}
			PlaceableShipObject val2 = (((Object)(object)val == (Object)null || (Object)(object)val.parentTo == (Object)null) ? null : ((Component)val.parentTo).GetComponentInChildren<PlaceableShipObject>(true));
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)val2 == (Object)null || (Object)(object)val2.parentObject == (Object)null)
			{
				return null;
			}
			int unlockableID = val2.unlockableID;
			UnlockableItem val3 = ((unlockableID >= 0 && unlockableID < instance.unlockablesList.unlockables.Count) ? instance.unlockablesList.unlockables[unlockableID] : null);
			DawnUnlockableItemInfo val4 = ((val3 != null) ? UnlockableItemExtensions.GetDawnInfo(val3) : null);
			string text = Utility.FurnitureParentPath(((Component)val2.parentObject).transform, parent);
			if (val3 != null && val4 != null && !val3.inStorage && text != null)
			{
				return new FurnitureRelationship(val, val2, parent, ((DawnBaseInfo<DawnUnlockableItemInfo>)(object)val4).TypedKey, text);
			}
			return null;
		}

		internal static bool RestoreParent(StartOfRound round, GrabbableObject item, SavedItemPose pose, out PlaceableShipObject? shipObject, out Transform? parent, out bool unresolved)
		{
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: 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.IsExcluded(key))
			{
				return false;
			}
			shipObject = ((IEnumerable<PlaceableShipObject>)Object.FindObjectsByType<PlaceableShipObject>((FindObjectsSortMode)0)).FirstOrDefault((Func<PlaceableShipObject, bool>)delegate(PlaceableShipObject candidate)
			{
				int unlockableID = candidate.unlockableID;
				if (unlockableID >= 0 && unlockableID < round.unlockablesList.unlockables.Count)
				{
					UnlockableItem obj = round.unlockablesList.unlockables[unlockableID];
					if (obj == null)
					{
						return false;
					}
					return ((object)((DawnBaseInfo<DawnUnlockableItemInfo>)(object)UnlockableItemExtensions.GetDawnInfo(obj))?.TypedKey).Equals((object?)key) == true;
				}
				return 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((PlaceableObjectsSurface surface) => (Object)(object)ParentFor(surface) == (Object)(object)parent).ToArray();
			found = array.Length != 0;
			if (array.Length < 2)
			{
				return array.FirstOrDefault();
			}
			float offset = 0f;
			if ((Object)(object)item.itemProperties != (Object)null)
			{
				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();
		}

		private static Transform? ParentFor(PlaceableObjectsSurface surface)
		{
			Transform val = null;
			if ((Object)(object)surface.parentTo != (Object)null)
			{
				val = ((Component)surface.parentTo).transform;
			}
			PlayerPhysicsRegion val2 = null;
			if ((Object)(object)val != (Object)null)
			{
				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))
			{
				return val;
			}
			return val2.physicsTransform;
		}
	}
	internal static class ShipItemSaving
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static Action<GrabbableObject, JObject> <0>__SaveItem;

			public static Action<GrabbableObject, JObject> <1>__ReadItem;

			public static Action<StartOfRound> <2>__RoundStarted;

			public static Action<StartOfRound> <3>__ItemsLoaded;

			public static Action<StartOfRound> <4>__UnlockablesSynced;

			public static Action <5>__GameSaved;

			public static Action<StartOfRound> <6>__RoundCleared;

			public static HatNetMessageHandler <7>__AssignmentsReceived;

			public static HatNetMessageHandler <8>__AssignmentsRequested;

			public static Action<GrabbableObject> <9>__MarkBasedItem;
		}

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

		private const float NetworkWaitSeconds = 8f;

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

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

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

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

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

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

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

		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 HashSet<Item> missingVariantDataLogged = new HashSet<Item>();

		private static bool unlockablesSynced;

		private static int savedPositions;

		private static int savedMeshVariants;

		private static int savedMaterialVariants;

		private static int savedBatteries;

		private static int savedChemicals;

		private static int savedSprayPaint;

		internal static void Init()
		{
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Expected O, but got Unknown
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Expected O, but got Unknown
			DawnItemSaveEvents.OnCollectExtraSaveData += SaveItem;
			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.<7>__AssignmentsReceived;
			if (obj == null)
			{
				HatNetMessageHandler val = AssignmentsReceived;
				<>O.<7>__AssignmentsReceived = val;
				obj = (object)val;
			}
			HatNet.AddClientHandler(assignmentKey, (HatNetMessageHandler)obj);
			NamespacedKey requestKey = RequestKey;
			object obj2 = <>O.<8>__AssignmentsRequested;
			if (obj2 == null)
			{
				HatNetMessageHandler val2 = AssignmentsRequested;
				<>O.<8>__AssignmentsRequested = val2;
				obj2 = (object)val2;
			}
			HatNet.AddServerHandler(requestKey, (HatNetMessageHandler)obj2);
		}

		private static void SaveItem(GrabbableObject item, JObject extraData)
		{
			extraData[((object)ItemIdKey).ToString()] = JToken.op_Implicit(((NetworkBehaviour)item).NetworkObjectId.ToString());
			WarnAboutMissingVariantData(item);
			Mesh[] meshVariants = item.itemProperties.meshVariants;
			Material[] materialVariants = item.itemProperties.materialVariants;
			int num = -1;
			MeshFilter val = default(MeshFilter);
			if (meshVariants != null && ((Component)item).TryGetComponent<MeshFilter>(ref val))
			{
				num = Array.IndexOf(meshVariants, val.sharedMesh);
				if (num >= 0)
				{
					extraData[((object)MeshVariantKey).ToString()] = JToken.op_Implicit(num);
					savedMeshVariants++;
				}
			}
			int num2 = -1;
			MeshRenderer val2 = default(MeshRenderer);
			if (materialVariants != null && ((Component)item).TryGetComponent<MeshRenderer>(ref val2))
			{
				num2 = Array.IndexOf(materialVariants, ((Renderer)val2).sharedMaterial);
				if (num2 >= 0)
				{
					extraData[((object)MaterialVariantKey).ToString()] = JToken.op_Implicit(num2);
					savedMaterialVariants++;
				}
			}
			float num3 = -1f;
			if (item.itemProperties.requiresBattery && item.insertedBattery != null)
			{
				num3 = Mathf.Clamp01(item.insertedBattery.charge);
				extraData[((object)BatteryChargeKey).ToString()] = JToken.op_Implicit(num3);
				savedBatteries++;
			}
			float num4 = -1f;
			GrabbableObject obj = item;
			TetraChemicalItem val3 = (TetraChemicalItem)(object)((obj is TetraChemicalItem) ? obj : null);
			if (val3 != null)
			{
				num4 = Mathf.Clamp01(val3.fuel);
				extraData[((object)ChemicalFuelKey).ToString()] = JToken.op_Implicit(num4);
				savedChemicals++;
			}
			float num5 = -1f;
			GrabbableObject obj2 = item;
			SprayPaintItem val4 = (SprayPaintItem)(object)((obj2 is SprayPaintItem) ? obj2 : null);
			if (val4 != null)
			{
				num5 = Mathf.Clamp01(val4.sprayCanTank);
				extraData[((object)SprayPaintTankKey).ToString()] = JToken.op_Implicit(num5);
				savedSprayPaint++;
			}
			BeltBagItem val5 = ((IEnumerable<BeltBagItem>)Object.FindObjectsByType<BeltBagItem>((FindObjectsSortMode)0)).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));
				savedPositions++;
				Forget(item);
				NetworkManager singleton = NetworkManager.Singleton;
				NetworkObject networkObject = ((NetworkBehaviour)item).NetworkObject;
				NetworkObject networkObject2 = ((NetworkBehaviour)val5).NetworkObject;
				if (singleton.IsServer && networkObject.IsSpawned && networkObject2.IsSpawned)
				{
					hostItems[((NetworkBehaviour)item).NetworkObjectId] = new ItemAssignment(item, savedItemPose)
					{
						ParentNetworkId = ((NetworkBehaviour)val5).NetworkObjectId,
						MeshVariant = num,
						MaterialVariant = num2,
						BatteryCharge = num3,
						ChemicalFuel = num4,
						SprayPaintTank = num5
					};
				}
				return;
			}
			FurnitureRelationship furniture;
			SavedItemPose savedItemPose2 = PoseFor(item, out furniture);
			if (savedItemPose2 == null)
			{
				extraData.Remove(((object)PoseKey).ToString());
				Forget(item);
				if (NetworkManager.Singleton.IsServer && ((NetworkBehaviour)item).NetworkObject.IsSpawned && (num >= 0 || num2 >= 0 || num3 >= 0f || num4 >= 0f || num5 >= 0f))
				{
					hostItems[((NetworkBehaviour)item).NetworkObjectId] = new ItemAssignment(item, num, num2, num3, num4, num5);
				}
				return;
			}
			extraData[((object)PoseKey).ToString()] = (JToken)(object)JObject.Parse(JsonUtility.ToJson((object)savedItemPose2));
			savedPositions++;
			NetworkManager singleton2 = NetworkManager.Singleton;
			NetworkObject networkObject3 = ((NetworkBehaviour)item).NetworkObject;
			if (singleton2.IsServer && networkObject3.IsSpawned)
			{
				hostItems[((NetworkBehaviour)item).NetworkObjectId] = new ItemAssignment(item, savedItemPose2, furniture)
				{
					MeshVariant = num,
					MaterialVariant = num2,
					BatteryCharge = num3,
					ChemicalFuel = num4,
					SprayPaintTank = num5
				};
				PlaceableObjectsSurfaceRegistry.MarkBasedItem(item);
			}
		}

		private static void ReadItem(GrabbableObject item, JObject extraData)
		{
			WarnAboutMissingVariantData(item);
			Mesh[] meshVariants = item.itemProperties.meshVariants;
			Material[] materialVariants = item.itemProperties.materialVariants;
			int item2 = ((meshVariants == null) ? (-1) : ReadVariantIndex(item, extraData, MeshVariantKey, meshVariants.Length, "mesh"));
			int item3 = ((materialVariants == null) ? (-1) : ReadVariantIndex(item, extraData, MaterialVariantKey, materialVariants.Length, "material"));
			float item4 = ReadAmount(item, extraData, BatteryChargeKey, "battery charge");
			float item5 = ((item is TetraChemicalItem) ? ReadAmount(item, extraData, ChemicalFuelKey, "TZP fuel") : (-1f));
			float item6 = ((item is SprayPaintItem) ? ReadAmount(item, extraData, SprayPaintTankKey, "spray paint") : (-1f));
			NetworkManager singleton = NetworkManager.Singleton;
			if (!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 val = extraData[((object)PoseKey).ToString()];
			if (val == null)
			{
				return;
			}
			try
			{
				SavedItemPose savedItemPose = JsonUtility.FromJson<SavedItemPose>(((object)val).ToString());
				bool flag = savedItemPose == null;
				bool flag2 = flag;
				if (!flag2)
				{
					ItemParentKind parentKind = savedItemPose.parentKind;
					bool flag3 = ((parentKind < ItemParentKind.Ship || parentKind > ItemParentKind.BeltBag) ? true : false);
					flag2 = flag3;
				}
				if (!flag2)
				{
					pending[item] = savedItemPose;
					if (savedItemPose.parentKind != ItemParentKind.BeltBag)
					{
						PlaceableObjectsSurfaceRegistry.MarkBasedItem(item);
					}
				}
			}
			catch (ArgumentException)
			{
				Log.Warn("Saved item position could not be read");
			}
		}

		private static void WarnAboutMissingVariantData(GrabbableObject item)
		{
			bool flag = item.itemProperties.meshVariants == null;
			bool flag2 = item.itemProperties.materialVariants == null;
			if ((flag || flag2) && missingVariantDataLogged.Add(item.itemProperties))
			{
				string text = ((flag && flag2) ? "mesh and material" : (flag ? "mesh" : "material"));
				Log.Warn(item.itemProperties.itemName + " has no " + text + " variant data. The affected variants will not be saved or restored.");
			}
		}

		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(item.itemProperties.itemName + " has invalid saved " + kind + " variant data");
				return -1;
			}
			int num = val.ToObject<int>();
			if (num < 0 || num >= variantCount)
			{
				Log.Warn($"{item.itemProperties.itemName} has invalid saved {kind} variant index {num}");
				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(item.itemProperties.itemName + " has invalid saved " + kind + " data");
				return -1f;
			}
			float num = val.ToObject<float>();
			if (num < 0f || num > 1f)
			{
				Log.Warn($"{item.itemProperties.itemName} has invalid saved {kind} value {num}");
				return -1f;
			}
			return num;
		}

		private static void ItemsLoaded(StartOfRound round)
		{
			if (((NetworkBehaviour)round).IsServer)
			{
				((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)
			{
				LogSavedItems();
			}
		}

		private static IEnumerator RestoreHost(StartOfRound round)
		{
			yield return _waitForSeconds0_5;
			ItemAssignmentList restored = new ItemAssignmentList();
			int unresolvedCount = 0;
			Dictionary<GrabbableObject, Animator[]> animators = pending.Keys.ToDictionary((GrabbableObject item) => item, (GrabbableObject item) => ((Component)item).GetComponentsInChildren<Animator>());
			Dictionary<GrabbableObject, float> animationFinishedAt = new Dictionary<GrabbableObject, float>();
			HashSet<GrabbableObject> processed = new HashSet<GrabbableObject>();
			GrabbableObject key;
			while (processed.Count < pending.Count)
			{
				foreach (KeyValuePair<GrabbableObject, SavedItemPose> item in pending)
				{
					item.Deconstruct(out key, out var value);
					GrabbableObject val = key;
					SavedItemPose savedItemPose = value;
					if (processed.Contains(val))
					{
						continue;
					}
					Animator[] array = animators[val];
					if (array.Length != 0)
					{
						if (!animationFinishedAt.TryGetValue(val, out var value2))
						{
							if (!array.All(delegate(Animator animator)
							{
								//IL_0024: Unknown result type (might be due to invalid IL or missing references)
								//IL_0029: Unknown result type (might be due to invalid IL or missing references)
								if (Object.op_Implicit((Object)(object)animator) && ((Behaviour)animator).isActiveAndEnabled && !Mathf.Approximately(animator.speed, 0f))
								{
									AnimatorStateInfo currentAnimatorStateInfo = animator.GetCurrentAnimatorStateInfo(0);
									return ((AnimatorStateInfo)(ref currentAnimatorStateInfo)).normalizedTime >= 1f;
								}
								return true;
							}))
							{
								continue;
							}
							value2 = (animationFinishedAt[val] = Time.time + 0.1f);
						}
						if (Time.time < value2)
						{
							continue;
						}
					}
					processed.Add(val);
					bool unresolved = false;
					ItemAssignment itemAssignment;
					if (savedItemPose.parentKind == ItemParentKind.BeltBag)
					{
						if (loadedItems.TryGetValue(savedItemPose.parentItemId, out GrabbableObject value3))
						{
							BeltBagItem val2 = (BeltBagItem)(object)((value3 is BeltBagItem) ? value3 : null);
							if (val2 != null && !((Object)(object)val == (Object)(object)val2))
							{
								itemAssignment = new ItemAssignment(val, savedItemPose)
								{
									ParentNetworkId = ((NetworkBehaviour)val2).NetworkObjectId
								};
								if (!val2.objectsInBag.Contains(val))
								{
									val.heldByPlayerOnServer = true;
									val2.PutObjectInBagLocalClient(val);
								}
								goto IL_023d;
							}
						}
						itemAssignment = null;
					}
					else
					{
						itemAssignment = ((savedItemPose.parentKind != ItemParentKind.Ship) ? RestoreFurniture(round, val, savedItemPose, out unresolved) : ((!PlaceableObjectsSurfaceRegistry.IsExplicitlyIgnored(val) && GrabbablePlacement.Place(val, round.elevatorTransform, savedItemPose.worldPosition, savedItemPose.worldRotation)) ? new ItemAssignment(val, savedItemPose) : null));
					}
					goto IL_023d;
					IL_023d:
					if (itemAssignment == null)
					{
						if (savedItemPose.parentKind == ItemParentKind.Furniture && unresolved)
						{
							unresolvedCount++;
						}
						Forget(val);
						continue;
					}
					if (pendingItemValues.TryGetValue(val, out (int, int, float, float, float) value4))
					{
						(itemAssignment.MeshVariant, itemAssignment.MaterialVariant, itemAssignment.BatteryCharge, itemAssignment.ChemicalFuel, itemAssignment.SprayPaintTank) = value4;
						pendingItemValues.Remove(val);
					}
					RestoreItemValues(val, itemAssignment, restored);
					hostItems[((NetworkBehaviour)val).NetworkObjectId] = itemAssignment;
					if (savedItemPose.parentKind != ItemParentKind.BeltBag)
					{
						PlaceableObjectsSurfaceRegistry.MarkBasedItem(val);
					}
					restored.Items.Add(itemAssignment);
					if (array.Length != 0)
					{
						Log.Debug("Restored animated item " + val.itemProperties.itemName + " position");
					}
				}
				if (processed.Count < pending.Count)
				{
					yield return null;
				}
			}
			foreach (KeyValuePair<GrabbableObject, (int, int, float, float, float)> pendingItemValue in pendingItemValues)
			{
				pendingItemValue.Deconstruct(out key, out var value5);
				GrabbableObject val3 = key;
				(int, int, float, float, float) tuple2 = value5;
				if (tuple2.Item1 >= 0 || tuple2.Item2 >= 0 || !(tuple2.Item3 < 0f) || !(tuple2.Item4 < 0f) || !(tuple2.Item5 < 0f))
				{
					ItemAssignment itemAssignment2 = new ItemAssignment(val3, tuple2.Item1, tuple2.Item2, tuple2.Item3, tuple2.Item4, tuple2.Item5);
					RestoreItemValues(val3, itemAssignment2, restored);
					hostItems[((NetworkBehaviour)val3).NetworkObjectId] = itemAssignment2;
					restored.Variants.Add(itemAssignment2);
				}
			}
			if (unresolvedCount > 0)
			{
				Log.Warn($"{unresolvedCount} item positions could not be restored and were left unchanged");
			}
			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 (!HatNet.SendToClients(AssignmentKey, JsonUtility.ToJson((object)restored)))
			{
				Log.Warn("Restored item data could not be sent to clients");
			}
			pending.Clear();
			pendingItemValues.Clear();
			loadedItems.Clear();
		}

		private static ItemAssignment? RestoreFurniture(StartOfRound round, GrabbableObject item, SavedItemPose pose, out bool unresolved)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			if (PlaceableObjectsSurfaceRegistry.IsExplicitlyIgnored(item))
			{
				unresolved = false;
				return null;
			}
			if (!FurnitureRelationship.RestoreParent(round, item, pose, out PlaceableShipObject shipObject, out Transform parent, out unresolved) || !Utility.PlaceFurniturePose(item, parent, pose.localPosition, pose.localRotation))
			{
				return null;
			}
			return new ItemAssignment(item, pose)
			{
				ParentNetworkId = ((NetworkBehaviour)shipObject.parentObject).NetworkObjectId
			};
		}

		private static SavedItemPose? PoseFor(GrabbableObject item, out FurnitureRelationship? furniture)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: 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_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: 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_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			furniture = null;
			if ((Object)(object)item == (Object)null || PlaceableObjectsSurfaceRegistry.IsExplicitlyIgnored(item))
			{
				return null;
			}
			bool flag = item.heldByPlayerOnServer || (Object)(object)item.playerHeldBy != (Object)null || (item.isHeld && !item.isHeldByEnemy);
			Vector3 worldPosition = (flag ? item.GetItemFloorPosition(default(Vector3)) : ((Component)item).transform.position);
			Quaternion worldRotation = ((Component)item).transform.rotation;
			if (flag)
			{
				Vector3 restingRotation = item.itemProperties.restingRotation;
				float num = (((Object)(object)item.playerHeldBy != (Object)null) ? ((float)((int)((Component)item.playerHeldBy).transform.localEulerAngles.y + item.itemProperties.floorYOffset) + 90f) : ((Component)item).transform.eulerAngles.y);
				worldRotation = Quaternion.Euler(restingRotation.x, num, restingRotation.z);
				Log.Debug("Saved held " + item.itemProperties.itemName + " at resting position on floor");
			}
			SavedItemPose savedItemPose = new SavedItemPose
			{
				parentKind = ItemParentKind.Ship,
				worldPosition = worldPosition,
				worldRotation = worldRotation
			};
			if (item is ClipboardItem || (item is PhysicsProp && (Object)(object)item.itemProperties != (Object)null && item.itemProperties.itemName == "Sticky note"))
			{
				return savedItemPose;
			}
			bool surfaceFound;
			FurnitureRelationship furnitureRelationship = FurnitureRelationship.FromItem(item, out surfaceFound);
			if (!surfaceFound)
			{
				return savedItemPose;
			}
			if (furnitureRelationship == null || ConfigManager.IsExcluded(furnitureRelationship.FurnitureKey) || (PlaceableObjectsSurfaceRegistry.TryGet(furnitureRelationship.Surface, out var registration) && !registration.AllowAutomaticSaving))
			{
				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)
		{
			NetworkManager singleton = NetworkManager.Singleton;
			if (singleton.IsServer)
			{
				hostItems.Remove(((NetworkBehaviour)item).NetworkObjectId);
				PlaceableObjectsSurfaceRegistry.UnmarkBasedItem(item);
			}
		}

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

		private static void RequestAssignments()
		{
			if (!HatNet.SendToServer(RequestKey, string.Empty))
			{
				Log.Warn("Current item data could not be requested");
			}
		}

		private static void AssignmentsRequested(ulong clientId, string data)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected O, but got Unknown
			int num = savedPositions;
			int num2 = savedMeshVariants;
			int num3 = savedMaterialVariants;
			int num4 = savedBatteries;
			int num5 = savedChemicals;
			int num6 = savedSprayPaint;
			hostItems.Clear();
			GrabbableObject[] array = Object.FindObjectsByType<GrabbableObject>((FindObjectsInactive)0, (FindObjectsSortMode)0);
			foreach (GrabbableObject val in array)
			{
				if (ItemExtensions.GetDawnInfo(val.itemProperties) != null)
				{
					SaveItem(val, new JObject());
				}
			}
			savedPositions = num;
			savedMeshVariants = num2;
			savedMaterialVariants = num3;
			savedBatteries = num4;
			savedChemicals = num5;
			savedSprayPaint = num6;
			ItemAssignmentList itemAssignmentList = new ItemAssignmentList
			{
				Items = hostItems.Values.Where((ItemAssignment assignment) => !assignment.ValuesOnly).ToList(),
				Variants = hostItems.Values.Where((ItemAssignment assignment) => assignment.ValuesOnly).ToList()
			};
			int num7 = hostItems.Values.Count((ItemAssignment assignment) => assignment.MeshVariant >= 0);
			int num8 = hostItems.Values.Count((ItemAssignment assignment) => assignment.MaterialVariant >= 0);
			int num9 = hostItems.Values.Count((ItemAssignment assignment) => assignment.BatteryCharge >= 0f);
			int num10 = hostItems.Values.Count((ItemAssignment assignment) => assignment.ChemicalFuel >= 0f);
			int num11 = hostItems.Values.Count((ItemAssignment assignment) => assignment.SprayPaintTank >= 0f);
			Log.Debug($"Sent {itemAssignmentList.Items.Count} item positions, {num7} mesh variants, {num8} material variants, {num9} battery charges, {num10} TZP fuel, and {num11} spray paint fuel to client {clientId}");
			if (!HatNet.SendToClient(AssignmentKey, clientId, JsonUtility.ToJson((object)itemAssignmentList)))
			{
				Log.Warn("Current item data could not be returned to a client");
			}
		}

		private static void AssignmentsReceived(ulong clientId, string data)
		{
			NetworkManager singleton = NetworkManager.Singleton;
			if (singleton.IsServer || clientId != 0L)
			{
				return;
			}
			try
			{
				ItemAssignmentList itemAssignmentList = JsonUtility.FromJson<ItemAssignmentList>(data);
				StartOfRound instance = StartOfRound.Instance;
				if (itemAssignmentList?.Items == null)
				{
					return;
				}
				foreach (ItemAssignment item in itemAssignmentList.Items)
				{
					if (item.Pose.parentKind != ItemParentKind.BeltBag)
					{
						NetworkRefs.WhenReady<GrabbableObject>((MonoBehaviour)(object)instance, item.ItemNetworkId, (Action<GrabbableObject>)PlaceableObjectsSurfaceRegistry.MarkBasedItem, 8f, 0);
					}
				}
				if (unlockablesSynced)
				{
					PlaceClientItems(instance, itemAssignmentList);
				}
			}
			catch (ArgumentException)
			{
				Log.Warn("Saved item data could not be read");
			}
		}

		private static void PlaceClientItems(StartOfRound round, ItemAssignmentList assignments)
		{
			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)
			{
				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)
				{
					NetworkRefs.WhenReady<BeltBagItem>((MonoBehaviour)(object)round, assignment2.ParentNetworkId, (Action<BeltBagItem>)delegate(BeltBagItem beltBag)
					{
						NetworkRefs.WhenReady<GrabbableObject>((MonoBehaviour)(object)round, assignment2.ItemNetworkId, (Action<GrabbableObject>)delegate(GrabbableObject item)
						{
							if (!beltBag.objectsInBag.Contains(item))
							{
								beltBag.PutObjectInBagLocalClient(item);
							}
							assignments.PlacedCount++;
						}, 8f, 4);
					}, 8f, 0);
					continue;
				}
				if (assignment2.Pose.parentKind == ItemParentKind.Ship)
				{
					NetworkRefs.WhenReady<GrabbableObject>((MonoBehaviour)(object)round, assignment2.ItemNetworkId, (Action<GrabbableObject>)delegate(GrabbableObject item)
					{
						//IL_004e: Unknown result type (might be due to invalid IL or missing references)
						//IL_005e: Unknown result type (might be due to invalid IL or missing references)
						assignments.PlacedCount += (GrabbablePlacement.Place((item.isHeld || (Object)(object)item.playerHeldBy != (Object)null || item.isHeldByEnemy) ? null : item, round.elevatorTransform, assignment2.Pose.worldPosition, assignment2.Pose.worldRotation) ? 1 : 0);
					}, 8f, 4);
					continue;
				}
				NetworkRefs.WhenReady((MonoBehaviour)(object)round, assignment2.ParentNetworkId, (Action<NetworkObject>)delegate(NetworkObject root)
				{
					Transform parent = Utility.FurnitureParentFromPath(((Component)root).transform, assignment2.Pose.furnitureParentPath);
					if (!((Object)(object)parent == (Object)null))
					{
						NetworkRefs.WhenReady<GrabbableObject>((MonoBehaviour)(object)round, assignment2.ItemNetworkId, (Action<GrabbableObject>)delegate(GrabbableObject item)
						{
							//IL_004e: 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)
							assignments.PlacedCount += (Utility.PlaceFurniturePose((item.isHeld || (Object)(object)item.playerHeldBy != (Object)null || item.isHeldByEnemy) ? null : item, parent, assignment2.Pose.localPosition, assignment2.Pose.localRotation) ? 1 : 0);
						}, 8f, 4);
					}
				}, 8f, 0);
			}
			((MonoBehaviour)round).StartCoroutine(ClientPlacementSummary(assignments));
		}

		private static IEnumerator ClientPlacementSummary(ItemAssignmentList assignments)
		{
			yield return (object)new WaitForSecondsRealtime(16.25f);
			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");
		}

		private static void RestoreItemValues(GrabbableObject item, ItemAssignment assignment, ItemAssignmentList assignments)
		{
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Expected O, but got Unknown
			Mesh[] meshVariants = item.itemProperties.meshVariants;
			Material[] materialVariants = item.itemProperties.materialVariants;
			string text = (NetworkManager.Singleton.IsServer ? "Host" : "Client");
			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++;
				}
				else
				{
					Log.Warn($"{item.itemProperties.itemName} mesh variant {assignment.MeshVariant} could not be restored");
				}
			}
			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++;
				}
				else
				{
					Log.Warn($"{item.itemProperties.itemName} material variant {assignment.MaterialVariant} could not be restored");
				}
			}
			if (assignment.BatteryCharge >= 0f)
			{
				item.insertedBattery = new Battery(assignment.BatteryCharge <= 0f, assignment.BatteryCharge);
				item.ChargeBatteries();
				assignments.BatteryCount++;
				Log.Debug($"{text} restored battery for {item.itemProperties.itemName} to {assignment.BatteryCharge * 100f:F1}% ({assignment.BatteryCharge:F4})");
			}
			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++;
					Log.Debug($"{text} restored TZP fuel for {item.itemProperties.itemName} to {assignment.ChemicalFuel * 100f:F1}% ({assignment.ChemicalFuel:F4})");
				}
				else
				{
					Log.Warn(item.itemProperties.itemName + " TZP fuel could not be restored");
				}
			}
			if (assignment.SprayPaintTank >= 0f)
			{
				SprayPaintItem val4 = (SprayPaintItem)(object)((item is SprayPaintItem) ? item : null);
				if (val4 != null)
				{
					val4.sprayCanTank = assignment.SprayPaintTank;
					assignments.SprayPaintCount++;
					Log.Debug($"{text} restored spray paint fuel for {item.itemProperties.itemName} to {assignment.SprayPaintTank * 100f:F1}% ({assignment.SprayPaintTank:F4})");
				}
				else
				{
					Log.Warn(item.itemProperties.itemName + " spray paint fuel could not be restored");
				}
			}
		}

		private static void RoundCleared(StartOfRound round)
		{
			pending.Clear();
			pendingItemValues.Clear();
			loadedItems.Clear();
			hostItems.Clear();
			PlaceableObjectsSurfaceRegistry.ClearRoundItems();
		}

		private static void LogSavedItems()
		{
			Log.Info($"Saved {savedPositions} item positions, {savedMeshVariants} mesh variants, {savedMaterialVariants} material variants, {savedBatteries} battery charges, {savedChemicals} TZP fuel, and {savedSprayPaint} spray paint fuel");
			savedPositions = 0;
			savedMeshVariants = 0;
			savedMaterialVariants = 0;
			savedBatteries = 0;
			savedChemicals = 0;
			savedSprayPaint = 0;
		}
	}
	internal enum ItemParentKind
	{
		Ship,
		Furniture,
		BeltBag
	}
	[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 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)
		{
			ItemNetworkId = ((NetworkBehaviour)item).NetworkObjectId;
			ParentNetworkId = ((furniture != null && (Object)(object)furniture.ShipObject.parentObject != (Object)null) ? ((NetworkBehaviour)furniture.ShipObject.parentObject).NetworkObjectId : 0);
			Pose = pose;
		}

		internal ItemAssignment(GrabbableObject item, int meshVariant, int materialVariant, float batteryCharge, float chemicalFuel, float sprayPaintTank)
		{
			ItemNetworkId = ((NetworkBehaviour)item).NetworkObjectId;
			MeshVariant = meshVariant;
			MaterialVariant = materialVariant;
			BatteryCharge = batteryCharge;
			ChemicalFuel = chemicalFuel;
			SprayPaintTank = sprayPaintTank;
			ValuesOnly = true;
		}
	}
	[Serializable]
	internal class ItemAssignmentList
	{
		public List<ItemAssignment> Items = new List<ItemAssignment>();

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

		internal int PlacedCount;

		internal int MeshVariantCount;

		internal int MaterialVariantCount;

		internal int BatteryCount;

		internal int ChemicalCount;

		internal int SprayPaintCount;
	}
}
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>();

		private static readonly HashSet<GrabbableObject> ignoredItems = new HashSet<GrabbableObject>();

		private 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;
		}

		internal static bool IsExplicitlyIgnored(GrabbableObject item)
		{
			if ((Object)(object)item != (Object)null)
			{
				return ignoredItems.Contains(item);
			}
			return false;
		}

		internal static void MarkBasedItem(GrabbableObject item)
		{
			basedItems.Add(item);
		}

		internal static void UnmarkBasedItem(GrabbableObject item)
		{
			basedItems.Remove(item);
		}

		internal static void ClearRoundItems()
		{
			ignoredItems.Clear();
			basedItems.Clear();
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}