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> excludedUnlockables = new HashSet>(); internal static void Bind(ConfigFile config) { ConfigEntry exclusions = config.Bind("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 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()); 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 furnitureKey, string parentPath) { internal PlaceableObjectsSurface Surface { get; } = surface; internal PlaceableShipObject ShipObject { get; } = shipObject; internal Transform ItemParent { get; } = itemParent; internal NamespacedKey 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((FindObjectsSortMode)0), out surfaceFound)); object obj; if (val == null) { obj = null; } else { NetworkObject parentTo = val.parentTo; obj = ((parentTo != null) ? ((Component)parentTo).GetComponentInChildren(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)(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 key = val.AsTyped(); if (ConfigManager.IsExcluded(key)) { return false; } shipObject = ((IEnumerable)Object.FindObjectsByType((FindObjectsSortMode)0)).FirstOrDefault((Func)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)(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(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)((Component)val).GetComponentsInChildren()).FirstOrDefault((Func)((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 list = new List(); 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 <0>__SaveItem; public static Action <1>__ReadItem; public static Action <2>__RoundStarted; public static Action <3>__ItemsLoaded; public static Action <4>__UnlockablesSynced; public static Action <5>__GameSaved; public static Action <6>__RoundCleared; public static HatNetMessageHandler <7>__AssignmentsReceived; public static HatNetMessageHandler <8>__AssignmentsRequested; public static Action <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 pending = new Dictionary(); private static readonly Dictionary hostItems = new Dictionary(); 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(((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 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(data); StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || itemAssignmentList?.Items == null) { return; } foreach (ItemAssignment item in itemAssignmentList.Items) { NetworkRefs.WhenReady((MonoBehaviour)(object)instance, item.ItemNetworkId, (Action)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((MonoBehaviour)(object)round, assignment.ItemNetworkId, (Action)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)delegate(NetworkObject root) { Transform parent = Utility.FurnitureParentFromPath(((Component)root).transform, assignment.Pose.furnitureParentPath); if (!((Object)(object)parent == (Object)null)) { NetworkRefs.WhenReady((MonoBehaviour)(object)round, assignment.ItemNetworkId, (Action)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 Items = new List(); 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 registrations = new List(); private static readonly HashSet ignoredItems = new HashSet(); private static readonly HashSet basedItems = new HashSet(); public static void Register(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(); } } }