Decompiled source of BasedItemSaving v0.0.1

BasedItemSaving.dll

Decompiled 9 hours 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.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("BasedItemSaving")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("0.0.1.0")]
[assembly: AssemblyInformationalVersion("0.0.1")]
[assembly: AssemblyProduct("BasedItemSaving")]
[assembly: AssemblyTitle("BasedItemSaving")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.1.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
{
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("MrHat.BasedItemSaving", "BasedItemSaving", "0.0.1")]
	internal class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource? mls;

		private void Awake()
		{
			mls = Logger.CreateLogSource("MrHat.BasedItemSaving");
			ConfigManager.Bind(((BaseUnityPlugin)this).Config);
			ShipItemSaving.Init();
			ManualLogSource? obj = mls;
			if (obj != null)
			{
				obj.LogInfo((object)"BasedItemSaving 0.0.1 loaded");
			}
		}
	}
	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,\nmelaniepocketroom:melmstockroomzero, melaniepocketroom:melmbedroomzero, melaniepocketroom:melmbedroomone,\nunknown_lib:basement, unknown_lib:house, unknown_lib:poolroom, unknown_lib:garden,\nmelaniecrafting:workbench, melaniecrafting:crucible,\nmelaniemeliciouscooked:grinder, melaniemeliciouscooked:blender, melaniemeliciouscooked:crockpot, melaniemeliciouscooked:toasteroven, melaniemeliciouscooked:portablegrill, melaniemeliciouscooked:oven, melaniemeliciouscooked:minifridge,\nmelaniemeliciouscooked:plantera, melaniemeliciouscooked:planterb, melaniemeliciouscooked:planterc, melaniemeliciouscooked:trellisa, melaniemeliciouscooked:trellisb, melaniemeliciouscooked:plantershelfa,\nmelaniemeliciouscooked: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>());
					continue;
				}
				ManualLogSource? mls = Plugin.mls;
				if (mls != null)
				{
					mls.LogError((object)("Excluded unlockable key " + text2 + " could not be read"));
				}
			}
		}
	}
	internal static class Log
	{
		internal static void Warn(string message)
		{
			ManualLogSource? mls = Plugin.mls;
			if (mls != null)
			{
				mls.LogWarning((object)message);
			}
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "BasedItemSaving";

		public const string PLUGIN_NAME = "BasedItemSaving";

		public const string PLUGIN_VERSION = "0.0.1";
	}
}
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_0021: Unknown result type (might be due to invalid IL or missing references)
			surfaceFound = false;
			Transform parent = ((Component)item).transform.parent;
			PlaceableObjectsSurface val = (((Object)(object)parent == (Object)null) ? null : SurfaceAt(parent, item, ((Component)item).transform.position, Object.FindObjectsByType<PlaceableObjectsSurface>((FindObjectsSortMode)0), out surfaceFound));
			object obj;
			if (val == null)
			{
				obj = null;
			}
			else
			{
				NetworkObject parentTo = val.parentTo;
				obj = ((parentTo != null) ? ((Component)parentTo).GetComponentInChildren<PlaceableShipObject>(true) : null);
			}
			PlaceableShipObject val2 = (PlaceableShipObject)obj;
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)val2?.parentObject == (Object)null || (Object)(object)instance?.unlockablesList == (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);
			return (val3 == null || val4 == null || val3.inStorage || text == null) ? null : new FurnitureRelationship(val, val2, parent, ((DawnBaseInfo<DawnUnlockableItemInfo>)(object)val4).TypedKey, text);
		}

		internal static bool RestoreParent(StartOfRound round, GrabbableObject item, SavedItemPose pose, out PlaceableShipObject? shipObject, out Transform? parent, out bool unresolved)
		{
			//IL_0113: 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)
			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;
				int result;
				if (unlockableID >= 0 && unlockableID < round.unlockablesList.unlockables.Count)
				{
					UnlockableItem obj2 = round.unlockablesList.unlockables[unlockableID];
					result = ((obj2 != null && ((object)((DawnBaseInfo<DawnUnlockableItemInfo>)(object)UnlockableItemExtensions.GetDawnInfo(obj2))?.TypedKey).Equals((object?)key) == true) ? 1 : 0);
				}
				else
				{
					result = 0;
				}
				return (byte)result != 0;
			});
			PlaceableShipObject? obj = shipObject;
			if (obj != null)
			{
				AutoParentToShip parentObject = obj.parentObject;
				if (((parentObject != null) ? new bool?(((NetworkBehaviour)parentObject).IsSpawned) : ((bool?)null)) == true && !round.unlockablesList.unlockables[shipObject.unlockableID].inStorage && !((Object)(object)(parent = Utility.FurnitureParentFromPath(((Component)shipObject.parentObject).transform, pose.furnitureParentPath)) == (Object)null))
				{
					bool found;
					PlaceableObjectsSurface val2 = SurfaceAt(parent, item, parent.TransformPoint(pose.localPosition), ((Component)shipObject.parentObject).GetComponentsInChildren<PlaceableObjectsSurface>(true), out found);
					PlaceableObjectsSurfaceRegistrationAPI registration;
					return (Object)(object)val2 == (Object)null || !PlaceableObjectsSurfaceRegistry.TryGet(val2, out registration) || registration.AllowAutomaticSaving;
				}
			}
			unresolved = true;
			return false;
		}

		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 = item.itemProperties?.verticalOffset ?? 0f;
			return array.Where((PlaceableObjectsSurface surface) => (Object)(object)surface.placeableBounds != (Object)null).OrderBy(delegate(PlaceableObjectsSurface surface)
			{
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				//IL_001f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				//IL_0029: 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_002c: 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_0032: Unknown result type (might be due to invalid IL or missing references)
				//IL_0037: Unknown result type (might be due to invalid IL or missing references)
				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)
		{
			object obj;
			if (surface == null)
			{
				obj = null;
			}
			else
			{
				NetworkObject parentTo = surface.parentTo;
				obj = ((parentTo != null) ? ((Component)parentTo).transform : null);
			}
			Transform val = (Transform)obj;
			PlayerPhysicsRegion val2 = ((val != null) ? ((IEnumerable<PlayerPhysicsRegion>)((Component)val).GetComponentsInChildren<PlayerPhysicsRegion>()).FirstOrDefault((Func<PlayerPhysicsRegion, bool>)((PlayerPhysicsRegion region) => ((Behaviour)region).isActiveAndEnabled)) : null);
			return (val2 != null && val2.allowDroppingItems && (Object)(object)val2.physicsTransform != (Object)null) ? val2.physicsTransform : val;
		}
	}
	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_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			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;
		}
	}
	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 const float NetworkWaitSeconds = 8f;

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

		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<ulong, ItemAssignment> hostItems = new Dictionary<ulong, ItemAssignment>();

		private static bool unlockablesSynced;

		internal static void Init()
		{
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Expected O, but got Unknown
			//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_014f: 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)
		{
			FurnitureRelationship furniture;
			SavedItemPose savedItemPose = PoseFor(item, out furniture);
			if (savedItemPose == null)
			{
				extraData.Remove(((object)PoseKey).ToString());
				Forget(item);
				return;
			}
			extraData[((object)PoseKey).ToString()] = (JToken)(object)JObject.Parse(JsonUtility.ToJson((object)savedItemPose));
			NetworkManager singleton = NetworkManager.Singleton;
			if (singleton != null && singleton.IsServer)
			{
				NetworkObject networkObject = ((NetworkBehaviour)item).NetworkObject;
				if (networkObject != null && networkObject.IsSpawned)
				{
					hostItems[((NetworkBehaviour)item).NetworkObjectId] = new ItemAssignment(item, savedItemPose, furniture);
					PlaceableObjectsSurfaceRegistry.MarkBasedItem(item);
				}
			}
		}

		private static void ReadItem(GrabbableObject item, JObject extraData)
		{
			NetworkManager singleton = NetworkManager.Singleton;
			if (singleton == null || !singleton.IsServer)
			{
				return;
			}
			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.Furniture) ? true : false);
					flag2 = flag3;
				}
				if (!flag2)
				{
					pending[item] = savedItemPose;
					PlaceableObjectsSurfaceRegistry.MarkBasedItem(item);
				}
			}
			catch (ArgumentException)
			{
				Log.Warn("Saved item pose could not be read");
			}
		}

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

		private static void GameSaved()
		{
			NetworkManager singleton = NetworkManager.Singleton;
			if (singleton != null && singleton.IsServer)
			{
				ManualLogSource? mls = Plugin.mls;
				if (mls != null)
				{
					mls.LogDebug((object)$"Saved {hostItems.Count} exact item poses");
				}
			}
		}

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

		private static IEnumerator RestoreHost(StartOfRound round)
		{
			for (int i = 0; i < 4; i++)
			{
				yield return null;
			}
			ItemAssignmentList restored = new ItemAssignmentList();
			int unresolvedCount = 0;
			foreach (KeyValuePair<GrabbableObject, SavedItemPose> item2 in pending)
			{
				item2.Deconstruct(out var key, out var value);
				GrabbableObject item = key;
				SavedItemPose pose = value;
				bool unresolved = false;
				ItemAssignment assignment = ((pose.parentKind != ItemParentKind.Ship) ? RestoreFurniture(round, item, pose, out unresolved) : ((!PlaceableObjectsSurfaceRegistry.IsExplicitlyIgnored(item) && GrabbablePlacement.Place(item, round.elevatorTransform, pose.worldPosition, pose.worldRotation)) ? new ItemAssignment(item, pose) : null));
				if (assignment == null)
				{
					if (pose.parentKind == ItemParentKind.Furniture && unresolved)
					{
						unresolvedCount++;
					}
					Forget(item);
				}
				else
				{
					hostItems[((NetworkBehaviour)item).NetworkObjectId] = assignment;
					PlaceableObjectsSurfaceRegistry.MarkBasedItem(item);
					restored.Items.Add(assignment);
				}
			}
			if (unresolvedCount > 0)
			{
				Log.Warn($"{unresolvedCount} furniture item relationships could not be resolved and Dawn poses were left unchanged");
			}
			ManualLogSource? mls = Plugin.mls;
			if (mls != null)
			{
				mls.LogDebug((object)$"Host restored {restored.Items.Count} of {pending.Count} saved item poses");
			}
			if (!HatNet.SendToClients(AssignmentKey, JsonUtility.ToJson((object)restored)))
			{
				Log.Warn("Restored item assignments could not be sent to clients");
			}
			pending.Clear();
		}

		private static ItemAssignment? RestoreFurniture(StartOfRound round, GrabbableObject item, SavedItemPose pose, out bool unresolved)
		{
			//IL_0028: 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)
			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)
			{
				FurnitureNetworkId = ((NetworkBehaviour)shipObject.parentObject).NetworkObjectId
			};
		}

		private static SavedItemPose? PoseFor(GrabbableObject item, out FurnitureRelationship? furniture)
		{
			//IL_0037: 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_0048: 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_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: 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)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: 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;
			}
			SavedItemPose savedItemPose = new SavedItemPose
			{
				parentKind = ItemParentKind.Ship,
				worldPosition = ((Component)item).transform.position,
				worldRotation = ((Component)item).transform.rotation
			};
			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 || 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)
		{
			if (!((Object)(object)item == (Object)null))
			{
				NetworkManager singleton = NetworkManager.Singleton;
				if (singleton != null && 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 assignments could not be requested");
			}
		}

		private static void AssignmentsRequested(ulong clientId, string data)
		{
			ItemAssignmentList itemAssignmentList = new ItemAssignmentList
			{
				Items = hostItems.Values.ToList()
			};
			if (!HatNet.SendToClient(AssignmentKey, clientId, JsonUtility.ToJson((object)itemAssignmentList)))
			{
				Log.Warn("Current item assignments could not be returned to a client");
			}
		}

		private static void AssignmentsReceived(ulong clientId, string data)
		{
			NetworkManager singleton = NetworkManager.Singleton;
			if ((singleton != null && singleton.IsServer) || clientId != 0)
			{
				return;
			}
			try
			{
				ItemAssignmentList itemAssignmentList = JsonUtility.FromJson<ItemAssignmentList>(data);
				StartOfRound instance = StartOfRound.Instance;
				if ((Object)(object)instance == (Object)null || itemAssignmentList?.Items == null)
				{
					return;
				}
				foreach (ItemAssignment item in itemAssignmentList.Items)
				{
					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 assignments could not be read");
			}
		}

		private static void PlaceClientItems(StartOfRound round, ItemAssignmentList assignments)
		{
			foreach (ItemAssignment assignment in assignments.Items)
			{
				if (assignment.Pose.parentKind == ItemParentKind.Ship)
				{
					NetworkRefs.WhenReady<GrabbableObject>((MonoBehaviour)(object)round, assignment.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, assignment.Pose.worldPosition, assignment.Pose.worldRotation) ? 1 : 0);
					}, 8f, 4);
					continue;
				}
				NetworkRefs.WhenReady((MonoBehaviour)(object)round, assignment.FurnitureNetworkId, (Action<NetworkObject>)delegate(NetworkObject root)
				{
					Transform parent = Utility.FurnitureParentFromPath(((Component)root).transform, assignment.Pose.furnitureParentPath);
					if (!((Object)(object)parent == (Object)null))
					{
						NetworkRefs.WhenReady<GrabbableObject>((MonoBehaviour)(object)round, assignment.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, assignment.Pose.localPosition, assignment.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);
			ManualLogSource? mls = Plugin.mls;
			if (mls != null)
			{
				mls.LogDebug((object)$"Client restored {assignments.PlacedCount} item poses with {assignments.Items.Count - assignments.PlacedCount} unresolved");
			}
		}

		private static void RoundCleared(StartOfRound round)
		{
			pending.Clear();
			hostItems.Clear();
			PlaceableObjectsSurfaceRegistry.ClearRoundItems();
		}
	}
	internal enum ItemParentKind
	{
		Ship,
		Furniture
	}
	[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;
	}
	[Serializable]
	internal class ItemAssignment
	{
		public ulong ItemNetworkId;

		public ulong FurnitureNetworkId;

		public SavedItemPose Pose = null;

		internal ItemAssignment(GrabbableObject item, SavedItemPose pose, FurnitureRelationship? furniture = null)
		{
			ItemNetworkId = ((NetworkBehaviour)item).NetworkObjectId;
			ulong? obj;
			if (furniture == null)
			{
				obj = null;
			}
			else
			{
				AutoParentToShip parentObject = furniture.ShipObject.parentObject;
				obj = ((parentObject != null) ? new ulong?(((NetworkBehaviour)parentObject).NetworkObjectId) : ((ulong?)null));
			}
			ulong? num = obj;
			FurnitureNetworkId = num.GetValueOrDefault();
			Pose = pose;
		}
	}
	[Serializable]
	internal class ItemAssignmentList
	{
		public List<ItemAssignment> Items = new List<ItemAssignment>();

		internal int PlacedCount;
	}
}
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)
		{
			return (Object)(object)item != (Object)null && (ignoredItems.Contains(item) || basedItems.Contains(item));
		}

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

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