Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of BasedItemSaving v1.2.0
BasedItemSaving.dll
Decompiled 2 weeks agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BasedItemSaving.API; using BasedItemSaving.Compat; using BasedItemSaving.Saving; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using Dawn; using Dawn.Internal; using HarmonyLib; using HatLib; using HatLib.Tools; using Microsoft.CodeAnalysis; using Newtonsoft.Json.Linq; using SelfSortingStorage.Cupboard; using Unity.Netcode; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("com.github.teamxiaolan.dawnlib")] [assembly: IgnoresAccessChecksTo("com.github.teamxiaolan.dawnlib.dusk")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("BasedItemSaving")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.2.0.0")] [assembly: AssemblyInformationalVersion("1.2.0")] [assembly: AssemblyProduct("BasedItemSaving")] [assembly: AssemblyTitle("BasedItemSaving")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.2.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BasedItemSaving { internal static class ConfigManager { internal static readonly HashSet<NamespacedKey<DawnUnlockableItemInfo>> ExcludedUnlockables = new HashSet<NamespacedKey<DawnUnlockableItemInfo>>(); internal static ConfigEntry<bool> SaveCruiserItems = null; internal static ConfigEntry<bool> VerboseLogging = null; internal static void Bind(ConfigFile config) { VerboseLogging = config.Bind<bool>("Internal", "Verbose Logging", false, "Enables verbose logging for debugging purposes"); SaveCruiserItems = config.Bind<bool>("Item Saving", "Save Cruiser Items", true, "Save and restore items inside the company cruiser"); ConfigEntry<string> exclusions = config.Bind<string>("Item Saving", "Excluded Unlockables", "misideitems:houseteleporter,melaniepocketroom:melmstockroomzero, melaniepocketroom:melmbedroomzero, melaniepocketroom:melmbedroomone, unknown_lib:basement, unknown_lib:house, unknown_lib:poolroom, unknown_lib:garden, melaniecrafting:workbench, melaniecrafting:crucible, melaniemeliciouscooked:grinder, melaniemeliciouscooked:blender, melaniemeliciouscooked:crockpot, melaniemeliciouscooked:toasteroven, melaniemeliciouscooked:portablegrill, melaniemeliciouscooked:oven, melaniemeliciouscooked:minifridge, melaniemeliciouscooked:plantera, melaniemeliciouscooked:planterb, melaniemeliciouscooked:planterc, melaniemeliciouscooked:trellisa, melaniemeliciouscooked:trellisb, melaniemeliciouscooked:plantershelfa, melaniemeliciouscooked:barrela, melaniemeliciouscooked:barrelb, melaniemeliciouscooked:wineracka, melaniemeliciouscooked:beeboxa, melaniemeliciouscooked:pota, melaniemeliciouscooked:icecreammaker, melaniemeliciouscooked:seedmaker", "Unlockables that should be ignored and excluded from item reparenting"); ReadExclusions(exclusions.Value); exclusions.SettingChanged += delegate { ReadExclusions(exclusions.Value); }; } private static void ReadExclusions(string value) { ExcludedUnlockables.Clear(); string[] array = value.Split(',', StringSplitOptions.RemoveEmptyEntries); NamespacedKey val = default(NamespacedKey); foreach (string text in array) { string text2 = text.Trim(); if (NamespacedKey.TryParse(text2, ref val)) { ExcludedUnlockables.Add(val.AsTyped<DawnUnlockableItemInfo>()); } else { Log.Warn("Skipped invalid excluded unlockable key " + text2); } } } } internal static class Log { internal static void Debug(string message) { Plugin.Mls.LogDebug((object)message); } internal static void Info(string message) { Plugin.Mls.LogInfo((object)message); } internal static void Warn(string message) { Plugin.Mls.LogWarning((object)message); } internal static void Error(string message) { Plugin.Mls.LogError((object)message); } internal static void Fatal(string message) { Plugin.Mls.LogFatal((object)message); } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("MrHat.BasedItemSaving", "BasedItemSaving", "1.2.0")] internal class Plugin : BaseUnityPlugin { private const string ModGuid = "MrHat.BasedItemSaving"; private const string ModName = "BasedItemSaving"; private const string ModVersion = "1.2.0"; private const string SelfSortingStorageGuid = "zigzag.SelfSortingStorage"; private const string PocketRoomsLibGuid = "MelanieMelicious.pocketRoomLib"; private const string V55CruiserGuid = "scandal.v55cruiser"; internal static ManualLogSource Mls; internal static readonly bool HasSelfSortingStorage = Chainloader.PluginInfos.ContainsKey("zigzag.SelfSortingStorage"); internal static readonly bool HasPocketRoomsLib = Chainloader.PluginInfos.ContainsKey("MelanieMelicious.pocketRoomLib"); internal static readonly bool HasV55Cruiser = Chainloader.PluginInfos.ContainsKey("scandal.v55cruiser"); private void Awake() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) Mls = Logger.CreateLogSource("MrHat.BasedItemSaving"); if (DawnConfig.DisableDawnItemSaving.Value) { Log.Error("BasedItemSaving could not start because DawnLib item saving is disabled"); return; } ConfigManager.Bind(((BaseUnityPlugin)this).Config); if (HasSelfSortingStorage) { new Harmony("MrHat.BasedItemSaving").PatchAll(typeof(SelfSortingStorageCompat)); Log.Info("SelfSortingStorage compatibility enabled"); } if (HasPocketRoomsLib) { Log.Info("PocketRoomsLib compatibility enabled"); } ShipItemSaving.Init(); if (ConfigManager.VerboseLogging.Value) { DawnItemSaveEvents.OnCollectExtraSaveData += VerboseLogging.ItemSaved; RoundEvents.OnRoundStart += VerboseLogging.CollidersFound; Utility.ItemRestored = (Action<GrabbableObject, SavedItemPose, Collider>)Delegate.Combine(Utility.ItemRestored, new Action<GrabbableObject, SavedItemPose, Collider>(VerboseLogging.ItemRestored)); Utility.ItemReturningToShip = (Action<GrabbableObject, SavedItemPose>)Delegate.Combine(Utility.ItemReturningToShip, new Action<GrabbableObject, SavedItemPose>(VerboseLogging.ItemReturningToShip)); ShipItemSaving.ItemPositionSkipped = (Action<GrabbableObject, string>)Delegate.Combine(ShipItemSaving.ItemPositionSkipped, new Action<GrabbableObject, string>(VerboseLogging.ItemPositionSkipped)); ShipItemSaving.ItemRemoved = (Action<GrabbableObject, string>)Delegate.Combine(ShipItemSaving.ItemRemoved, new Action<GrabbableObject, string>(VerboseLogging.ItemRemoved)); ShipItemSaving.ItemValueRestored = (Action<GrabbableObject, string, float>)Delegate.Combine(ShipItemSaving.ItemValueRestored, new Action<GrabbableObject, string, float>(VerboseLogging.ItemValueRestored)); ShipItemSaving.ItemsSynced = (Action<ItemAssignmentList, ulong?, bool>)Delegate.Combine(ShipItemSaving.ItemsSynced, new Action<ItemAssignmentList, ulong?, bool>(VerboseLogging.ItemsSynced)); if (HasPocketRoomsLib) { RoundEvents.OnRoundStart += VerboseLogging.PocketRoomsCollidersFound; } } Log.Info("BasedItemSaving 1.2.0 loaded"); } } internal static class Utility { internal static Action<GrabbableObject, SavedItemPose, Collider?>? ItemRestored; internal static Action<GrabbableObject, SavedItemPose>? ItemReturningToShip; private static Collider? SavingColliderAt(Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) VehicleController val = CruiserAt(position); if ((Object)(object)val != (Object)null) { return val.physicsRegion.itemDropCollider; } Collider shipInnerRoomBounds = StartOfRound.Instance.shipInnerRoomBounds; Bounds bounds; if (shipInnerRoomBounds.enabled && ((Component)shipInnerRoomBounds).gameObject.activeInHierarchy) { bounds = shipInnerRoomBounds.bounds; if (((Bounds)(ref bounds)).Contains(position)) { return shipInnerRoomBounds; } } foreach (Collider validItemSpawningCollider in ItemSaveDataHandler.ValidItemSpawningColliders) { if (!((Object)(object)validItemSpawningCollider != (Object)null) || validItemSpawningCollider == null || !validItemSpawningCollider.enabled) { continue; } GameObject gameObject = ((Component)validItemSpawningCollider).gameObject; if (gameObject != null && gameObject.activeInHierarchy) { bounds = validItemSpawningCollider.bounds; if (((Bounds)(ref bounds)).Contains(position)) { return validItemSpawningCollider; } } } if (Plugin.HasPocketRoomsLib) { return PocketRoomsLibCompat.SavingColliderAt(position); } return null; } internal static VehicleController? CruiserAt(Vector3 position, Transform? itemTransform = null) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) VehicleController attachedVehicle = StartOfRound.Instance.attachedVehicle; if ((Object)(object)attachedVehicle == (Object)null || !((NetworkBehaviour)attachedVehicle).IsSpawned) { return null; } if (!ConfigManager.SaveCruiserItems.Value || (attachedVehicle.vehicleID == 0 && Plugin.HasV55Cruiser)) { return null; } if ((Object)(object)itemTransform != (Object)null) { if (itemTransform.IsChildOf(((Component)attachedVehicle).transform)) { return attachedVehicle; } return null; } Collider itemDropCollider = attachedVehicle.physicsRegion.itemDropCollider; if ((Object)(object)itemDropCollider != (Object)null && itemDropCollider.ClosestPoint(position) == position) { return attachedVehicle; } return null; } internal static bool PlaceShipPose(GrabbableObject item, SavedItemPose pose, ItemAssignmentList assignments) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)item == (Object)null) { return false; } StartOfRound instance = StartOfRound.Instance; Collider val = SavingColliderAt(pose.worldPosition); bool flag = pose.parentKind != ItemParentKind.Ship || (Object)(object)val == (Object)null; if (flag) { ItemReturningToShip?.Invoke(item, pose); pose.parentKind = ItemParentKind.Ship; pose.worldPosition = item.GetItemFloorPosition(instance.elevatorTransform.TransformPoint(new Vector3(0f, 0f, -7f))); item.isInElevator = true; item.isInShipRoom = true; val = SavingColliderAt(pose.worldPosition); } if (!GrabbablePlacement.Place(item, instance.elevatorTransform, pose.worldPosition, pose.worldRotation)) { return false; } if (flag) { assignments.returnedToShipCount++; } if ((Object)(object)val != (Object)null) { assignments.colliderCounts.TryGetValue(((Object)val).name, out var value); assignments.colliderCounts[((Object)val).name] = value + 1; } ItemRestored?.Invoke(item, pose, val); return true; } internal static string? FurnitureParentPath(Transform root, Transform parent) { if ((Object)(object)parent == (Object)(object)root) { return string.Empty; } List<int> list = new List<int>(); Transform val = parent; while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root) { list.Add(val.GetSiblingIndex()); val = val.parent; } if ((Object)(object)val != (Object)(object)root) { return null; } list.Reverse(); return string.Join("/", list); } internal static Transform? FurnitureParentFromPath(Transform root, string path) { Transform val = root; if (string.IsNullOrWhiteSpace(path)) { return val; } string[] array = path.Split('/'); foreach (string s in array) { if (!int.TryParse(s, out var result) || result < 0 || result >= val.childCount) { return null; } val = val.GetChild(result); } return val; } internal static bool PlaceFurniturePose(GrabbableObject? item, Transform parent, SavedItemPose pose, ItemAssignmentList assignments) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)item == (Object)null) { return false; } Vector3 val = parent.TransformPoint(pose.localPosition); Collider val2 = SavingColliderAt(val); if ((Object)(object)val2 == (Object)null && pose.parentKind != ItemParentKind.Cruiser) { pose.parentKind = ItemParentKind.Ship; pose.worldPosition = val; return PlaceShipPose(item, pose, assignments); } if (!GrabbablePlacement.Place(item, parent, val, parent.rotation * pose.localRotation)) { return false; } ((Component)item).transform.SetLocalPositionAndRotation(pose.localPosition, pose.localRotation); item.targetFloorPosition = pose.localPosition; item.startFallingPosition = pose.localPosition + Vector3.up * 0.0001f; if ((Object)(object)val2 != (Object)null) { assignments.colliderCounts.TryGetValue(((Object)val2).name, out var value); assignments.colliderCounts[((Object)val2).name] = value + 1; } ItemRestored?.Invoke(item, pose, val2); return true; } } internal static class VerboseLogging { internal static void ItemSaved(GrabbableObject item, JObject extraData) { //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) string itemName = item.itemProperties.itemName; JToken val = extraData[((object)ShipItemSaving.RemoveOnLoadKey).ToString()]; if (val != null) { Log.Debug("Marked " + itemName + " for removal on load because it is " + Extensions.Value<string>((IEnumerable<JToken>)val)); return; } JToken val2 = extraData[((object)ShipItemSaving.PoseKey).ToString()]; if (val2 != null) { SavedItemPose savedItemPose = val2.ToObject<SavedItemPose>(); if (savedItemPose.parentKind == ItemParentKind.BeltBag) { Log.Debug("Saving " + itemName + " inside a belt bag"); } else if (savedItemPose.parentKind == ItemParentKind.Cruiser) { Log.Debug($"Saving {itemName} inside the cruiser at local position {savedItemPose.localPosition:F3} with rotation {((Quaternion)(ref savedItemPose.localRotation)).eulerAngles:F1}"); } else if (savedItemPose.parentKind == ItemParentKind.Furniture) { Log.Debug($"Saving {itemName} on {savedItemPose.furnitureKey} at local position {savedItemPose.localPosition:F3} with rotation {((Quaternion)(ref savedItemPose.localRotation)).eulerAngles:F1} and parent path '{savedItemPose.furnitureParentPath}'"); } else { Log.Debug($"Saving {itemName} at {savedItemPose.worldPosition:F3} with rotation {((Quaternion)(ref savedItemPose.worldRotation)).eulerAngles:F1}"); } } else if (item.deactivated) { Log.Debug("Skipped position saving for " + itemName + " because it is deactivated"); } else if (item.itemUsedUp) { Log.Debug("Skipped position saving for " + itemName + " because it is used up"); } JToken arg = default(JToken); if (extraData.TryGetValue(((object)ShipItemSaving.MeshVariantKey).ToString(), ref arg)) { Log.Debug($"Saving mesh variant {arg} for {itemName}"); } JToken arg2 = default(JToken); if (extraData.TryGetValue(((object)ShipItemSaving.MaterialVariantKey).ToString(), ref arg2)) { Log.Debug($"Saving material variant {arg2} for {itemName}"); } JToken val3 = default(JToken); if (extraData.TryGetValue(((object)ShipItemSaving.BatteryChargeKey).ToString(), ref val3)) { Log.Debug($"Saving battery charge for {itemName} at {Extensions.Value<float>((IEnumerable<JToken>)val3) * 100f:F1}%"); } JToken val4 = default(JToken); if (extraData.TryGetValue(((object)ShipItemSaving.ChemicalFuelKey).ToString(), ref val4)) { Log.Debug($"Saving TZP fuel for {itemName} at {Extensions.Value<float>((IEnumerable<JToken>)val4) * 100f:F1}%"); } JToken val5 = default(JToken); if (extraData.TryGetValue(((object)ShipItemSaving.SprayPaintTankKey).ToString(), ref val5)) { Log.Debug($"Saving spray paint/weed killer fuel for {itemName} at {Extensions.Value<float>((IEnumerable<JToken>)val5) * 100f:F1}%"); } } internal static void ItemRemoved(GrabbableObject item, string reason) { Log.Debug("Host removing saved " + item.itemProperties.itemName + " because it was " + reason); } internal static void ItemPositionSkipped(GrabbableObject item, string reason) { Log.Debug("Skipped position saving for " + item.itemProperties.itemName + " because " + reason); } internal static void ItemReturningToShip(GrabbableObject item, SavedItemPose pose) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) string text = "Client"; if (NetworkManager.Singleton.IsServer) { text = "Host"; } string text2 = "its saved position is outside the item saving bounds"; if (pose.parentKind == ItemParentKind.Cruiser) { text2 = "its saved cruiser could not be used"; } else if (pose.parentKind == ItemParentKind.Furniture) { text2 = "its saved furniture or parent could not be found"; } Log.Debug($"{text} returning {item.itemProperties.itemName} from {pose.worldPosition:F3} to the middle of the ship because {text2}"); } internal static void ItemRestored(GrabbableObject item, SavedItemPose pose, Collider? collider) { //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) string text = "Client"; if (NetworkManager.Singleton.IsServer) { text = "Host"; } if (pose.parentKind == ItemParentKind.BeltBag) { Log.Debug(text + " restored " + item.itemProperties.itemName + " inside a belt bag"); return; } string text2 = "in the ship"; if (pose.parentKind == ItemParentKind.Furniture) { text2 = "on " + pose.furnitureKey; } else if (pose.parentKind == ItemParentKind.Cruiser) { text2 = "in the cruiser"; } string text3 = string.Empty; if ((Object)(object)collider != (Object)null) { text3 = " inside " + ((Object)collider).name; } Log.Debug($"{text} restored {item.itemProperties.itemName} {text2} at {((Component)item).transform.position:F3} with rotation {((Component)item).transform.eulerAngles:F1}{text3}"); } internal static void ItemValueRestored(GrabbableObject item, string kind, float value) { string text = "Client"; if (NetworkManager.Singleton.IsServer) { text = "Host"; } if (kind == "mesh variant" || kind == "material variant") { Log.Debug($"{text} restored {kind} {value:F0} for {item.itemProperties.itemName}"); } else { Log.Debug($"{text} restored {kind} for {item.itemProperties.itemName} to {value * 100f:F1}%"); } } internal static void ItemsSynced(ItemAssignmentList assignments, ulong? clientId, bool sent) { //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) string text = "Received"; string text2 = "from the host"; if (sent) { text = "Sent"; text2 = "to all clients"; if (clientId.HasValue) { text2 = $"to client {clientId.Value}"; } } foreach (ItemAssignment item in assignments.items) { SavedItemPose pose = item.pose; if (pose.parentKind == ItemParentKind.BeltBag) { Log.Debug(text + " " + item.itemName + " inside a belt bag " + text2); } else if (pose.parentKind == ItemParentKind.Cruiser) { Log.Debug($"{text} {item.itemName} inside the cruiser at local position {pose.localPosition:F3} with rotation {((Quaternion)(ref pose.localRotation)).eulerAngles:F1} {text2}"); } else if (pose.parentKind == ItemParentKind.Furniture) { Log.Debug($"{text} {item.itemName} on {pose.furnitureKey} at local position {pose.localPosition:F3} with rotation {((Quaternion)(ref pose.localRotation)).eulerAngles:F1} and parent path '{pose.furnitureParentPath}' {text2}"); } else { Log.Debug($"{text} {item.itemName} position {pose.worldPosition:F3} with rotation {((Quaternion)(ref pose.worldRotation)).eulerAngles:F1} {text2}"); } } foreach (ItemAssignment item2 in assignments.items.Concat(assignments.variants)) { if (item2.meshVariant >= 0) { Log.Debug($"{text} mesh variant {item2.meshVariant} for {item2.itemName} {text2}"); } if (item2.materialVariant >= 0) { Log.Debug($"{text} material variant {item2.materialVariant} for {item2.itemName} {text2}"); } if (item2.batteryCharge >= 0f) { Log.Debug($"{text} battery charge for {item2.itemName} at {item2.batteryCharge * 100f:F1}% {text2}"); } if (item2.chemicalFuel >= 0f) { Log.Debug($"{text} TZP fuel for {item2.itemName} at {item2.chemicalFuel * 100f:F1}% {text2}"); } if (item2.sprayPaintTank >= 0f) { Log.Debug($"{text} spray paint/weed killer fuel for {item2.itemName} at {item2.sprayPaintTank * 100f:F1}% {text2}"); } } } internal static void CollidersFound(StartOfRound round) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) Collider shipInnerRoomBounds = round.shipInnerRoomBounds; string name = ((Object)shipInnerRoomBounds).name; Bounds bounds = shipInnerRoomBounds.bounds; object arg = ((Bounds)(ref bounds)).center; bounds = shipInnerRoomBounds.bounds; Log.Debug($"Found ship bounds {name} at {arg:F3} with size {((Bounds)(ref bounds)).size:F3}"); foreach (Collider validItemSpawningCollider in ItemSaveDataHandler.ValidItemSpawningColliders) { if ((Object)(object)validItemSpawningCollider != (Object)null) { string name2 = ((Object)validItemSpawningCollider).name; bounds = validItemSpawningCollider.bounds; object arg2 = ((Bounds)(ref bounds)).center; bounds = validItemSpawningCollider.bounds; Log.Debug($"Found DawnLib item saving bounds {name2} at {arg2:F3} with size {((Bounds)(ref bounds)).size:F3}"); } } VehicleController attachedVehicle = round.attachedVehicle; if ((Object)(object)attachedVehicle != (Object)null && ((NetworkBehaviour)attachedVehicle).IsSpawned && ConfigManager.SaveCruiserItems.Value && (attachedVehicle.vehicleID != 0 || !Plugin.HasV55Cruiser)) { Collider itemDropCollider = attachedVehicle.physicsRegion.itemDropCollider; if ((Object)(object)itemDropCollider != (Object)null) { object[] obj = new object[4] { attachedVehicle.vehicleID, ((Object)itemDropCollider).name, null, null }; bounds = itemDropCollider.bounds; obj[2] = ((Bounds)(ref bounds)).center; bounds = itemDropCollider.bounds; obj[3] = ((Bounds)(ref bounds)).size; Log.Debug(string.Format("Found cruiser {0} item saving bounds {1} at {2:F3} with size {3:F3}", obj)); } } } [MethodImpl(MethodImplOptions.NoInlining)] internal static void PocketRoomsCollidersFound(StartOfRound round) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) foreach (Collider roomBound in MelShipBoundsCheck.roomBounds) { if ((Object)(object)roomBound != (Object)null) { string name = ((Object)roomBound).name; Bounds bounds = roomBound.bounds; object arg = ((Bounds)(ref bounds)).center; bounds = roomBound.bounds; Log.Debug($"Found PocketRoomsLib item saving bounds {name} at {arg:F3} with size {((Bounds)(ref bounds)).size:F3}"); } } } } public static class MyPluginInfo { public const string PLUGIN_GUID = "BasedItemSaving"; public const string PLUGIN_NAME = "BasedItemSaving"; public const string PLUGIN_VERSION = "1.2.0"; } } namespace BasedItemSaving.Saving { internal class FurnitureRelationship(PlaceableObjectsSurface surface, PlaceableShipObject shipObject, Transform itemParent, NamespacedKey<DawnUnlockableItemInfo> furnitureKey, string parentPath) { internal PlaceableObjectsSurface Surface { get; } = surface; internal PlaceableShipObject ShipObject { get; } = shipObject; internal Transform ItemParent { get; } = itemParent; internal NamespacedKey<DawnUnlockableItemInfo> FurnitureKey { get; } = furnitureKey; internal string ParentPath { get; } = parentPath; internal static FurnitureRelationship? FromItem(GrabbableObject item, out bool surfaceFound) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) surfaceFound = false; Transform parent = ((Component)item).transform.parent; if ((Object)(object)parent == (Object)null) { return null; } NetworkObject componentInParent = ((Component)parent).GetComponentInParent<NetworkObject>(); if ((Object)(object)componentInParent == (Object)null) { return null; } PlaceableObjectsSurface val = SurfaceAt(parent, item, ((Component)item).transform.position, ((Component)componentInParent).GetComponentsInChildren<PlaceableObjectsSurface>(), out surfaceFound); if ((Object)(object)val == (Object)null) { return null; } PlaceableShipObject componentInChildren = ((Component)val.parentTo).GetComponentInChildren<PlaceableShipObject>(true); if ((Object)(object)componentInChildren == (Object)null || (Object)(object)componentInChildren.parentObject == (Object)null) { return null; } StartOfRound instance = StartOfRound.Instance; int unlockableID = componentInChildren.unlockableID; if (unlockableID < 0 || unlockableID >= instance.unlockablesList.unlockables.Count) { return null; } UnlockableItem val2 = instance.unlockablesList.unlockables[unlockableID]; if (val2.inStorage) { return null; } DawnUnlockableItemInfo dawnInfo = UnlockableItemExtensions.GetDawnInfo(val2); string text = Utility.FurnitureParentPath(((Component)componentInChildren.parentObject).transform, parent); if (dawnInfo == null || text == null) { return null; } return new FurnitureRelationship(val, componentInChildren, parent, ((DawnBaseInfo<DawnUnlockableItemInfo>)(object)dawnInfo).TypedKey, text); } internal static bool RestoreParent(StartOfRound round, GrabbableObject item, SavedItemPose pose, out PlaceableShipObject? shipObject, out Transform? parent, out bool unresolved) { //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) shipObject = null; parent = null; unresolved = false; NamespacedKey val = default(NamespacedKey); if (!NamespacedKey.TryParse(pose.furnitureKey, ref val)) { unresolved = true; return false; } NamespacedKey<DawnUnlockableItemInfo> key = val.AsTyped<DawnUnlockableItemInfo>(); if (ConfigManager.ExcludedUnlockables.Contains(key)) { return false; } shipObject = ((IEnumerable<PlaceableShipObject>)UnlockableSaveDataHandler.placeableShipObjects).FirstOrDefault((Func<PlaceableShipObject, bool>)delegate(PlaceableShipObject candidate) { if (!((Component)candidate).gameObject.activeInHierarchy) { return false; } int unlockableID = candidate.unlockableID; if (unlockableID < 0 || unlockableID >= round.unlockablesList.unlockables.Count) { return false; } UnlockableItem val3 = round.unlockablesList.unlockables[unlockableID]; return ((object)((DawnBaseInfo<DawnUnlockableItemInfo>)(object)UnlockableItemExtensions.GetDawnInfo(val3))?.TypedKey).Equals((object?)key) ?? false; }); if ((Object)(object)shipObject == (Object)null || (Object)(object)shipObject.parentObject == (Object)null || !((NetworkBehaviour)shipObject.parentObject).IsSpawned || round.unlockablesList.unlockables[shipObject.unlockableID].inStorage || (Object)(object)(parent = Utility.FurnitureParentFromPath(((Component)shipObject.parentObject).transform, pose.furnitureParentPath)) == (Object)null) { unresolved = true; return false; } bool found; PlaceableObjectsSurface val2 = SurfaceAt(parent, item, parent.TransformPoint(pose.localPosition), ((Component)shipObject.parentObject).GetComponentsInChildren<PlaceableObjectsSurface>(true), out found); if (!((Object)(object)val2 == (Object)null) && PlaceableObjectsSurfaceRegistry.TryGet(val2, out var registration)) { return registration.AllowAutomaticSaving; } return true; } private static PlaceableObjectsSurface? SurfaceAt(Transform parent, GrabbableObject item, Vector3 position, PlaceableObjectsSurface[] surfaces, out bool found) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) PlaceableObjectsSurface[] array = surfaces.Where(delegate(PlaceableObjectsSurface surface) { if ((Object)(object)surface.parentTo == (Object)null) { return false; } Transform val = ((Component)surface.parentTo).transform; PlayerPhysicsRegion val2 = ((IEnumerable<PlayerPhysicsRegion>)((Component)val).GetComponentsInChildren<PlayerPhysicsRegion>()).FirstOrDefault((Func<PlayerPhysicsRegion, bool>)((PlayerPhysicsRegion candidate) => ((Behaviour)candidate).isActiveAndEnabled)); if ((Object)(object)val2 != (Object)null && val2.allowDroppingItems && (Object)(object)val2.physicsTransform != (Object)null) { val = val2.physicsTransform; } return (Object)(object)val == (Object)(object)parent; }).ToArray(); found = array.Length != 0; if (array.Length < 2) { return array.FirstOrDefault(); } float offset = item.itemProperties.verticalOffset; return array.Where((PlaceableObjectsSurface surface) => (Object)(object)surface.placeableBounds != (Object)null).OrderBy(delegate(PlaceableObjectsSurface surface) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) Collider placeableBounds = surface.placeableBounds; Vector3 val = position - ((Component)placeableBounds).transform.up * offset; Vector3 val2 = placeableBounds.ClosestPoint(val) - val; return ((Vector3)(ref val2)).sqrMagnitude; }).FirstOrDefault(); } } internal static class ShipItemSaving { [CompilerGenerated] private static class <>O { public static Action<GrabbableObject, JObject> <0>__ReadItem; public static Action<StartOfRound> <1>__RoundStarted; public static Action<StartOfRound> <2>__ItemsLoaded; public static Action<StartOfRound> <3>__UnlockablesSynced; public static Action <4>__GameSaved; public static Action<StartOfRound> <5>__RoundCleared; public static HatNetMessageHandler <6>__AssignmentsReceived; public static HatNetMessageHandler <7>__AssignmentsRequested; } private static readonly WaitForSecondsRealtime waitForSecondsRealtime1625 = new WaitForSecondsRealtime(16.25f); private static readonly WaitForSeconds waitForSeconds05 = new WaitForSeconds(0.5f); internal static readonly NamespacedKey PoseKey = NamespacedKey.From("baseditemsaving", "exact_pose"); private static readonly NamespacedKey ItemIdKey = NamespacedKey.From("baseditemsaving", "item_id"); internal static readonly NamespacedKey MeshVariantKey = NamespacedKey.From("baseditemsaving", "mesh_variant"); internal static readonly NamespacedKey MaterialVariantKey = NamespacedKey.From("baseditemsaving", "material_variant"); internal static readonly NamespacedKey BatteryChargeKey = NamespacedKey.From("baseditemsaving", "battery_charge"); internal static readonly NamespacedKey ChemicalFuelKey = NamespacedKey.From("baseditemsaving", "chemical_fuel"); internal static readonly NamespacedKey SprayPaintTankKey = NamespacedKey.From("baseditemsaving", "spray_paint_tank"); internal static readonly NamespacedKey RemoveOnLoadKey = NamespacedKey.From("baseditemsaving", "remove_on_load"); private static readonly NamespacedKey AssignmentKey = NamespacedKey.From("baseditemsaving", "item_assignments"); private static readonly NamespacedKey RequestKey = NamespacedKey.From("baseditemsaving", "item_assignments_request"); private static readonly Dictionary<GrabbableObject, SavedItemPose> pending = new Dictionary<GrabbableObject, SavedItemPose>(); private static readonly Dictionary<GrabbableObject, (int Mesh, int Material, float Battery, float Chemical, float SprayPaint)> pendingItemValues = new Dictionary<GrabbableObject, (int, int, float, float, float)>(); private static readonly Dictionary<string, GrabbableObject> loadedItems = new Dictionary<string, GrabbableObject>(); private static readonly Dictionary<ulong, ItemAssignment> hostItems = new Dictionary<ulong, ItemAssignment>(); private static readonly Dictionary<GrabbableObject, JObject> collectedItems = new Dictionary<GrabbableObject, JObject>(); private static Coroutine? _restoreCoroutine; private static bool _unlockablesSynced; internal static Action<GrabbableObject, string, float>? ItemValueRestored; internal static Action<GrabbableObject, string>? ItemPositionSkipped; internal static Action<GrabbableObject, string>? ItemRemoved; internal static Action<ItemAssignmentList, ulong?, bool>? ItemsSynced; internal static void Init() { //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Expected O, but got Unknown //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Expected O, but got Unknown DawnItemSaveEvents.OnCollectExtraSaveData += delegate(GrabbableObject item, JObject extraData) { SaveItem(item, extraData, ItemSaveDataHandler.AllShipItems); collectedItems[item] = extraData; }; DawnItemSaveEvents.OnLoadExtraSaveData += ReadItem; RoundEvents.OnRoundStart += RoundStarted; RoundEvents.OnShipItemsLoaded += ItemsLoaded; RoundEvents.OnShipUnlockablesSynced += UnlockablesSynced; RoundEvents.OnGameSaved += GameSaved; RoundEvents.OnLocalDisconnect += RoundCleared; RoundEvents.OnDestroy += RoundCleared; NamespacedKey assignmentKey = AssignmentKey; object obj = <>O.<6>__AssignmentsReceived; if (obj == null) { HatNetMessageHandler val = AssignmentsReceived; <>O.<6>__AssignmentsReceived = val; obj = (object)val; } HatNet.AddClientHandler(assignmentKey, (HatNetMessageHandler)obj); NamespacedKey requestKey = RequestKey; object obj2 = <>O.<7>__AssignmentsRequested; if (obj2 == null) { HatNetMessageHandler val2 = AssignmentsRequested; <>O.<7>__AssignmentsRequested = val2; obj2 = (object)val2; } HatNet.AddServerHandler(requestKey, (HatNetMessageHandler)obj2); } private static void SaveItem(GrabbableObject item, JObject extraData, GrabbableObject[] items) { float num = -1f; GrabbableObject obj = item; TetraChemicalItem val = (TetraChemicalItem)(object)((obj is TetraChemicalItem) ? obj : null); if (val != null) { num = Mathf.Clamp01(val.fuel); extraData[((object)ChemicalFuelKey).ToString()] = JToken.op_Implicit(num); } float num2 = -1f; GrabbableObject obj2 = item; SprayPaintItem val2 = (SprayPaintItem)(object)((obj2 is SprayPaintItem) ? obj2 : null); if (val2 != null) { num2 = Mathf.Clamp01(val2.sprayCanTank); extraData[((object)SprayPaintTankKey).ToString()] = JToken.op_Implicit(num2); } if (item.deactivated || item.itemUsedUp) { if (item.deactivated) { extraData[((object)RemoveOnLoadKey).ToString()] = JToken.op_Implicit("deactivated"); } else if (num < 0f && num2 < 0f) { extraData[((object)RemoveOnLoadKey).ToString()] = JToken.op_Implicit("used up"); } Forget(item); if (NetworkManager.Singleton.IsServer && ((NetworkBehaviour)item).NetworkObject.IsSpawned && (num >= 0f || num2 >= 0f)) { hostItems[((NetworkBehaviour)item).NetworkObjectId] = new ItemAssignment(item, -1, -1, -1f, num, num2); } return; } extraData[((object)ItemIdKey).ToString()] = JToken.op_Implicit(((NetworkBehaviour)item).NetworkObjectId.ToString()); Mesh[] meshVariants = item.itemProperties.meshVariants; Material[] materialVariants = item.itemProperties.materialVariants; int num3 = -1; MeshFilter val3 = default(MeshFilter); if (meshVariants != null && ((Component)item).TryGetComponent<MeshFilter>(ref val3)) { num3 = Array.IndexOf(meshVariants, val3.sharedMesh); if (num3 >= 0) { extraData[((object)MeshVariantKey).ToString()] = JToken.op_Implicit(num3); } } int num4 = -1; MeshRenderer val4 = default(MeshRenderer); if (materialVariants != null && ((Component)item).TryGetComponent<MeshRenderer>(ref val4)) { num4 = Array.IndexOf(materialVariants, ((Renderer)val4).sharedMaterial); if (num4 >= 0) { extraData[((object)MaterialVariantKey).ToString()] = JToken.op_Implicit(num4); } } float num5 = -1f; if (item.itemProperties.requiresBattery && item.insertedBattery != null) { num5 = Mathf.Clamp01(item.insertedBattery.charge); extraData[((object)BatteryChargeKey).ToString()] = JToken.op_Implicit(num5); } BeltBagItem val5 = items.OfType<BeltBagItem>().FirstOrDefault((Func<BeltBagItem, bool>)((BeltBagItem candidate) => candidate.objectsInBag.Contains(item))); if ((Object)(object)val5 != (Object)null) { SavedItemPose savedItemPose = new SavedItemPose { parentKind = ItemParentKind.BeltBag, parentItemId = ((NetworkBehaviour)val5).NetworkObjectId.ToString() }; extraData[((object)PoseKey).ToString()] = (JToken)(object)JObject.Parse(JsonUtility.ToJson((object)savedItemPose)); Forget(item); if (NetworkManager.Singleton.IsServer && ((NetworkBehaviour)item).NetworkObject.IsSpawned && ((NetworkBehaviour)val5).NetworkObject.IsSpawned) { hostItems[((NetworkBehaviour)item).NetworkObjectId] = new ItemAssignment(item, savedItemPose) { parentNetworkId = ((NetworkBehaviour)val5).NetworkObjectId, meshVariant = num3, materialVariant = num4, batteryCharge = num5, chemicalFuel = num, sprayPaintTank = num2 }; } return; } FurnitureRelationship furniture; VehicleController cruiser; SavedItemPose savedItemPose2 = PoseFor(item, out furniture, out cruiser); if (savedItemPose2 == null) { extraData.Remove(((object)PoseKey).ToString()); Forget(item); if (NetworkManager.Singleton.IsServer && ((NetworkBehaviour)item).NetworkObject.IsSpawned && (num3 >= 0 || num4 >= 0 || num5 >= 0f || num >= 0f || num2 >= 0f)) { hostItems[((NetworkBehaviour)item).NetworkObjectId] = new ItemAssignment(item, num3, num4, num5, num, num2); } return; } extraData[((object)PoseKey).ToString()] = (JToken)(object)JObject.Parse(JsonUtility.ToJson((object)savedItemPose2)); if (NetworkManager.Singleton.IsServer && ((NetworkBehaviour)item).NetworkObject.IsSpawned) { ItemAssignment itemAssignment = new ItemAssignment(item, savedItemPose2, furniture) { meshVariant = num3, materialVariant = num4, batteryCharge = num5, chemicalFuel = num, sprayPaintTank = num2 }; if ((Object)(object)cruiser != (Object)null) { itemAssignment.parentNetworkId = ((NetworkBehaviour)cruiser).NetworkObjectId; } hostItems[((NetworkBehaviour)item).NetworkObjectId] = itemAssignment; PlaceableObjectsSurfaceRegistry.BasedItems.Add(item); } } private static void ReadItem(GrabbableObject item, JObject extraData) { JToken val = extraData[((object)RemoveOnLoadKey).ToString()]; if (val != null) { if (NetworkManager.Singleton.IsServer) { string reason = Extensions.Value<string>((IEnumerable<JToken>)val); if (reason == "deactivated") { item.deactivated = true; } else { item.itemUsedUp = true; } NetworkRefs.WhenReady<GrabbableObject>((MonoBehaviour)(object)StartOfRound.Instance, ((NetworkBehaviour)item).NetworkObjectId, (Action<GrabbableObject>)delegate(GrabbableObject removedItem) { ItemRemoved?.Invoke(removedItem, reason); ((NetworkBehaviour)removedItem).NetworkObject.Despawn(true); }, 8f, 4); } return; } Mesh[] meshVariants = item.itemProperties.meshVariants; Material[] materialVariants = item.itemProperties.materialVariants; int item2 = -1; if (meshVariants != null) { item2 = ReadVariantIndex(item, extraData, MeshVariantKey, meshVariants.Length, "mesh"); } int item3 = -1; if (materialVariants != null) { item3 = ReadVariantIndex(item, extraData, MaterialVariantKey, materialVariants.Length, "material"); } float item4 = ReadAmount(item, extraData, BatteryChargeKey, "battery charge"); float item5 = -1f; if (item is TetraChemicalItem) { item5 = ReadAmount(item, extraData, ChemicalFuelKey, "TZP fuel"); } float item6 = -1f; if (item is SprayPaintItem) { item6 = ReadAmount(item, extraData, SprayPaintTankKey, "spray paint"); } if (!NetworkManager.Singleton.IsServer) { return; } pendingItemValues[item] = (item2, item3, item4, item5, item6); JToken obj = extraData[((object)ItemIdKey).ToString()]; string text = ((obj != null) ? obj.ToObject<string>() : null); if (!string.IsNullOrWhiteSpace(text)) { loadedItems[text] = item; } JToken val2 = extraData[((object)PoseKey).ToString()]; if (val2 == null) { return; } try { SavedItemPose savedItemPose = JsonUtility.FromJson<SavedItemPose>(((object)val2).ToString()); bool flag = savedItemPose == null; bool flag2 = flag; if (!flag2) { ItemParentKind parentKind = savedItemPose.parentKind; bool flag3 = ((parentKind < ItemParentKind.Ship || parentKind > ItemParentKind.Cruiser) ? true : false); flag2 = flag3; } if (!flag2) { pending[item] = savedItemPose; if (savedItemPose.parentKind != ItemParentKind.BeltBag) { PlaceableObjectsSurfaceRegistry.BasedItems.Add(item); } } } catch (ArgumentException) { Log.Warn("Skipped unreadable saved position for " + item.itemProperties.itemName); } } private static int ReadVariantIndex(GrabbableObject item, JObject extraData, NamespacedKey key, int variantCount, string kind) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 JToken val = extraData[((object)key).ToString()]; if (val == null) { return -1; } if ((int)val.Type != 6) { Log.Warn("Skipped invalid saved " + kind + " variant data for " + item.itemProperties.itemName); return -1; } int num = val.ToObject<int>(); if (num < 0 || num >= variantCount) { Log.Warn($"Skipped invalid saved {kind} variant index {num} for {item.itemProperties.itemName}"); return -1; } return num; } private static float ReadAmount(GrabbableObject item, JObject extraData, NamespacedKey key, string kind) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Invalid comparison between Unknown and I4 //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 JToken val = extraData[((object)key).ToString()]; if (val == null) { return -1f; } JTokenType type = val.Type; if ((int)type != 7 && (int)type != 6) { Log.Warn("Skipped invalid saved " + kind + " data for " + item.itemProperties.itemName); return -1f; } float num = val.ToObject<float>(); if (num < 0f || num > 1f) { Log.Warn($"Skipped invalid saved {kind} value {num} for {item.itemProperties.itemName}"); return -1f; } return num; } private static void ItemsLoaded(StartOfRound round) { if (((NetworkBehaviour)round).IsServer) { _restoreCoroutine = ((MonoBehaviour)round).StartCoroutine(RestoreHost(round)); } } private static void RoundStarted(StartOfRound round) { _unlockablesSynced = false; if (!((NetworkBehaviour)round).IsServer) { RequestAssignments(); } } private static void GameSaved() { if (NetworkManager.Singleton.IsHost) { JObject[] source = (from item in ItemSaveDataHandler.AllShipItems where (Object)(object)item != (Object)null && collectedItems.ContainsKey(item) select collectedItems[item]).ToArray(); int num = source.Count((JObject extraData) => extraData[((object)PoseKey).ToString()] != null); int num2 = source.Count((JObject extraData) => extraData[((object)MeshVariantKey).ToString()] != null); int num3 = source.Count((JObject extraData) => extraData[((object)MaterialVariantKey).ToString()] != null); int num4 = source.Count((JObject extraData) => extraData[((object)BatteryChargeKey).ToString()] != null); int num5 = source.Count((JObject extraData) => extraData[((object)ChemicalFuelKey).ToString()] != null); int num6 = source.Count((JObject extraData) => extraData[((object)SprayPaintTankKey).ToString()] != null); if (num > 0 || num2 > 0 || num3 > 0 || num4 > 0 || num5 > 0 || num6 > 0) { Log.Info($"Saved {num} item positions, {num2} mesh variants, {num3} material variants, {num4} battery charges, {num5} TZP fuel, and {num6} spray paint fuel"); } collectedItems.Clear(); } } private static IEnumerator RestoreHost(StartOfRound round) { yield return waitForSeconds05; while (Plugin.HasSelfSortingStorage && SelfSortingStorageCompat.Reloading) { yield return null; } ItemAssignmentList restored = new ItemAssignmentList(); GrabbableObject key; (int, int, float, float, float) value; if (Plugin.HasSelfSortingStorage) { KeyValuePair<GrabbableObject, (int, int, float, float, float)>[] array = pendingItemValues.ToArray(); foreach (KeyValuePair<GrabbableObject, (int, int, float, float, float)> keyValuePair in array) { keyValuePair.Deconstruct(out key, out value); GrabbableObject val = key; (int, int, float, float, float) tuple = value; if (!((Object)(object)val == (Object)null) && SelfSortingStorageCompat.OwnsPosition(val)) { if (pending.Remove(val)) { Log.Debug("Skipped saved position for " + val.itemProperties.itemName + " because SelfSortingStorage owns it"); } Forget(val); pendingItemValues.Remove(val); if (tuple.Item1 >= 0 || tuple.Item2 >= 0 || !(tuple.Item3 < 0f) || !(tuple.Item4 < 0f) || !(tuple.Item5 < 0f)) { ItemAssignment itemAssignment = new ItemAssignment(val, tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4, tuple.Item5); RestoreItemValues(val, itemAssignment, restored); hostItems[((NetworkBehaviour)val).NetworkObjectId] = itemAssignment; restored.variants.Add(itemAssignment); } } } } int missingFurnitureCount = 0; Dictionary<GrabbableObject, Animator[]> animators = pending.Keys.Where((GrabbableObject item) => (Object)(object)item != (Object)null).ToDictionary((GrabbableObject item) => item, (GrabbableObject item) => ((Component)item).GetComponentsInChildren<Animator>()); Dictionary<GrabbableObject, float> animationFinishedAt = new Dictionary<GrabbableObject, float>(); HashSet<GrabbableObject> processed = new HashSet<GrabbableObject>(); float summaryAt = Time.realtimeSinceStartup + 10f; bool progressLogged = false; bool waiting = true; while (waiting) { waiting = false; foreach (KeyValuePair<GrabbableObject, SavedItemPose> item in pending) { item.Deconstruct(out key, out var value2); GrabbableObject val2 = key; SavedItemPose savedItemPose = value2; if ((Object)(object)val2 == (Object)null || processed.Contains(val2)) { continue; } Animator[] array2 = animators[val2]; if (array2.Length != 0) { if (val2.hasBeenHeld || val2.heldByPlayerOnServer || val2.isHeld || (Object)(object)val2.playerHeldBy != (Object)null || val2.isHeldByEnemy) { processed.Add(val2); Forget(val2); Log.Debug("Skipped saved position for animated item " + val2.itemProperties.itemName + " because it was picked up before restoration"); continue; } if (!animationFinishedAt.TryGetValue(val2, out var value3)) { if (!array2.All(delegate(Animator animator) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)animator == (Object)null) && ((Behaviour)animator).isActiveAndEnabled && !Mathf.Approximately(animator.speed, 0f)) { AnimatorStateInfo currentAnimatorStateInfo2 = animator.GetCurrentAnimatorStateInfo(0); return ((AnimatorStateInfo)(ref currentAnimatorStateInfo2)).normalizedTime >= 1f; } return true; })) { waiting = true; continue; } value3 = (animationFinishedAt[val2] = Time.realtimeSinceStartup + 0.1f); } if (Time.realtimeSinceStartup < value3) { waiting = true; continue; } } processed.Add(val2); ItemAssignment itemAssignment2 = null; if (savedItemPose.parentKind == ItemParentKind.BeltBag) { if (loadedItems.TryGetValue(savedItemPose.parentItemId, out GrabbableObject value4)) { BeltBagItem val3 = (BeltBagItem)(object)((value4 is BeltBagItem) ? value4 : null); if (val3 != null && (Object)(object)val2 != (Object)(object)val3) { itemAssignment2 = new ItemAssignment(val2, savedItemPose) { parentNetworkId = ((NetworkBehaviour)val3).NetworkObjectId }; if (!val3.objectsInBag.Contains(val2)) { val2.heldByPlayerOnServer = true; val3.PutObjectInBagLocalClient(val2); } Utility.ItemRestored?.Invoke(val2, savedItemPose, null); } } } else if (!PlaceableObjectsSurfaceRegistry.IgnoredItems.Contains(val2)) { PlaceableShipObject shipObject; Transform parent; bool unresolved; if (savedItemPose.parentKind == ItemParentKind.Ship) { if (Utility.PlaceShipPose(val2, savedItemPose, restored)) { itemAssignment2 = new ItemAssignment(val2, savedItemPose); } } else if (savedItemPose.parentKind == ItemParentKind.Cruiser) { VehicleController attachedVehicle = round.attachedVehicle; if ((Object)(object)attachedVehicle != (Object)null && ((NetworkBehaviour)attachedVehicle).IsSpawned && ConfigManager.SaveCruiserItems.Value && (attachedVehicle.vehicleID != 0 || !Plugin.HasV55Cruiser)) { if (Utility.PlaceFurniturePose(val2, ((Component)attachedVehicle).transform, savedItemPose, restored)) { itemAssignment2 = new ItemAssignment(val2, savedItemPose) { parentNetworkId = ((NetworkBehaviour)attachedVehicle).NetworkObjectId }; } } else if (Utility.PlaceShipPose(val2, savedItemPose, restored)) { itemAssignment2 = new ItemAssignment(val2, savedItemPose); if (!ConfigManager.SaveCruiserItems.Value) { Log.Debug("Returned " + val2.itemProperties.itemName + " to ship because cruiser item saving is disabled"); } else if ((Object)(object)attachedVehicle != (Object)null && attachedVehicle.vehicleID == 0 && Plugin.HasV55Cruiser) { Log.Debug("Returned " + val2.itemProperties.itemName + " to ship because v55 cruiser is installed"); } else { Log.Warn("Returned " + val2.itemProperties.itemName + " to ship because cruiser could not be found"); } } } else if (FurnitureRelationship.RestoreParent(round, val2, savedItemPose, out shipObject, out parent, out unresolved)) { if (Utility.PlaceFurniturePose(val2, parent, savedItemPose, restored)) { itemAssignment2 = new ItemAssignment(val2, savedItemPose); if (savedItemPose.parentKind == ItemParentKind.Furniture) { itemAssignment2.parentNetworkId = ((NetworkBehaviour)shipObject.parentObject).NetworkObjectId; } } } else if (unresolved && Utility.PlaceShipPose(val2, savedItemPose, restored)) { itemAssignment2 = new ItemAssignment(val2, savedItemPose); missingFurnitureCount++; } } if (itemAssignment2 == null) { Forget(val2); continue; } if (pendingItemValues.TryGetValue(val2, out (int, int, float, float, float) value5)) { (itemAssignment2.meshVariant, itemAssignment2.materialVariant, itemAssignment2.batteryCharge, itemAssignment2.chemicalFuel, itemAssignment2.sprayPaintTank) = value5; pendingItemValues.Remove(val2); } RestoreItemValues(val2, itemAssignment2, restored); hostItems[((NetworkBehaviour)val2).NetworkObjectId] = itemAssignment2; if (savedItemPose.parentKind != ItemParentKind.BeltBag) { PlaceableObjectsSurfaceRegistry.BasedItems.Add(val2); } restored.items.Add(itemAssignment2); } if (!waiting) { continue; } if (!progressLogged && Time.realtimeSinceStartup >= summaryAt) { int num2 = pending.Keys.Count((GrabbableObject item) => (Object)(object)item != (Object)null && !processed.Contains(item)); Log.Info($"Restored {restored.items.Count} item positions, {restored.meshVariantCount} mesh variants, {restored.materialVariantCount} material variants, {restored.batteryCount} battery charges, {restored.chemicalCount} TZP fuel and {restored.sprayPaintCount} spray paint/weed killer fuel. Awaiting animations on {num2} items"); if (restored.colliderCounts.Count > 0) { Log.Debug("Restored " + string.Join(", ", restored.colliderCounts.Select<KeyValuePair<string, int>, string>((KeyValuePair<string, int> entry) => $"{entry.Value} item positions inside {entry.Key}"))); } if (restored.returnedToShipCount > 0) { Log.Debug($"Returned {restored.returnedToShipCount} items to the middle of the ship"); } foreach (GrabbableObject key2 in pending.Keys) { if ((Object)(object)key2 == (Object)null || processed.Contains(key2)) { continue; } if (animationFinishedAt.TryGetValue(key2, out var value6) && Time.realtimeSinceStartup < value6) { Log.Debug("Awaiting animated item " + key2.itemProperties.itemName + " to finish its 0.1 second settling delay"); continue; } bool flag = false; Animator[] array3 = animators[key2]; foreach (Animator val4 in array3) { if (!((Object)(object)val4 == (Object)null) && ((Behaviour)val4).isActiveAndEnabled) { AnimatorStateInfo currentAnimatorStateInfo = val4.GetCurrentAnimatorStateInfo(0); if (!Mathf.Approximately(val4.speed, 0f) && !(((AnimatorStateInfo)(ref currentAnimatorStateInfo)).normalizedTime >= 1f)) { Log.Debug($"Awaiting animated item {key2.itemProperties.itemName} child {((Object)val4).name} at normalised time {((AnimatorStateInfo)(ref currentAnimatorStateInfo)).normalizedTime:G9} with speed {val4.speed:G9}"); flag = true; } } } if (!flag) { Log.Debug("Awaiting animated item " + key2.itemProperties.itemName + " for the next restoration check"); } } progressLogged = true; } yield return null; } foreach (KeyValuePair<GrabbableObject, (int, int, float, float, float)> pendingItemValue in pendingItemValues) { pendingItemValue.Deconstruct(out key, out value); GrabbableObject val5 = key; (int, int, float, float, float) tuple3 = value; if (!((Object)(object)val5 == (Object)null) && (tuple3.Item1 >= 0 || tuple3.Item2 >= 0 || !(tuple3.Item3 < 0f) || !(tuple3.Item4 < 0f) || !(tuple3.Item5 < 0f))) { ItemAssignment itemAssignment3 = new ItemAssignment(val5, tuple3.Item1, tuple3.Item2, tuple3.Item3, tuple3.Item4, tuple3.Item5); RestoreItemValues(val5, itemAssignment3, restored); hostItems[((NetworkBehaviour)val5).NetworkObjectId] = itemAssignment3; restored.variants.Add(itemAssignment3); } } if (missingFurnitureCount > 0) { Log.Warn($"Moved {missingFurnitureCount} items to the ship because their saved furniture or parent could not be found"); } if (restored.items.Count > 0 || restored.variants.Count > 0) { Log.Info($"Restored {restored.items.Count} item positions, {restored.meshVariantCount} mesh variants, {restored.materialVariantCount} material variants, {restored.batteryCount} battery charges, {restored.chemicalCount} TZP fuel and {restored.sprayPaintCount} spray paint fuel"); if (restored.colliderCounts.Count > 0) { Log.Debug("Restored " + string.Join(", ", restored.colliderCounts.Select<KeyValuePair<string, int>, string>((KeyValuePair<string, int> entry) => $"{entry.Value} item positions inside {entry.Key}"))); } if (restored.returnedToShipCount > 0) { Log.Debug($"Returned {restored.returnedToShipCount} items to the middle of the ship"); } if (NetworkManager.Singleton.IsListening) { if (HatNet.SendToClients(AssignmentKey, JsonUtility.ToJson((object)restored))) { ItemsSynced?.Invoke(restored, null, arg3: true); } else { Log.Error("Restored item data could not be sent to clients"); } } } pending.Clear(); pendingItemValues.Clear(); loadedItems.Clear(); _restoreCoroutine = null; } private static SavedItemPose? PoseFor(GrabbableObject item, out FurnitureRelationship? furniture, out VehicleController? cruiser) { //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0312: Unknown result type (might be due to invalid IL or missing references) //IL_0317: Unknown result type (might be due to invalid IL or missing references) //IL_031c: Unknown result type (might be due to invalid IL or missing references) //IL_0329: Unknown result type (might be due to invalid IL or missing references) //IL_032e: Unknown result type (might be due to invalid IL or missing references) //IL_0339: Unknown result type (might be due to invalid IL or missing references) //IL_033e: Unknown result type (might be due to invalid IL or missing references) //IL_0343: Unknown result type (might be due to invalid IL or missing references) furniture = null; cruiser = null; if (PlaceableObjectsSurfaceRegistry.IgnoredItems.Contains(item)) { ItemPositionSkipped?.Invoke(item, "it was ignored through the API"); return null; } if (Plugin.HasSelfSortingStorage && SelfSortingStorageCompat.OwnsPosition(item)) { ItemPositionSkipped?.Invoke(item, "SelfSortingStorage owns it"); return null; } bool flag = item.heldByPlayerOnServer || (Object)(object)item.playerHeldBy != (Object)null || (item != null && item.isHeld && !item.isHeldByEnemy); Vector3 val = default(Vector3); Quaternion val2 = default(Quaternion); ((Component)item).transform.GetPositionAndRotation(ref val, ref val2); if (flag) { cruiser = StartOfRound.Instance.attachedVehicle; Vector3 val3 = default(Vector3); if ((Object)(object)cruiser != (Object)null && ((NetworkBehaviour)cruiser).IsSpawned && ConfigManager.SaveCruiserItems.Value && (cruiser.vehicleID != 0 || !Plugin.HasV55Cruiser) && (Object)(object)item.GetPhysicsRegionOfDroppedObject(item.playerHeldBy, ref val3, true) == (Object)(object)((NetworkBehaviour)cruiser).NetworkObject) { val = cruiser.physicsRegion.physicsTransform.TransformPoint(val3); } else { cruiser = null; val = item.GetItemFloorPosition(default(Vector3)); } Vector3 restingRotation = item.itemProperties.restingRotation; float num = ((Component)item).transform.eulerAngles.y; if ((Object)(object)item.playerHeldBy != (Object)null) { num = (float)((int)((Component)item.playerHeldBy).transform.localEulerAngles.y + item.itemProperties.floorYOffset) + 90f; } val2 = Quaternion.Euler(restingRotation.x, num, restingRotation.z); Log.Debug("Calculated floor position for held item " + item.itemProperties.itemName); } SavedItemPose savedItemPose = new SavedItemPose { parentKind = ItemParentKind.Ship, worldPosition = val, worldRotation = val2 }; if (!flag) { cruiser = Utility.CruiserAt(val, ((Component)item).transform); } if ((Object)(object)cruiser != (Object)null) { savedItemPose.parentKind = ItemParentKind.Cruiser; savedItemPose.localPosition = ((Component)cruiser).transform.InverseTransformPoint(val); savedItemPose.localRotation = Quaternion.Inverse(((Component)cruiser).transform.rotation) * val2; return savedItemPose; } if (item is ClipboardItem || (item is PhysicsProp && item.itemProperties.itemName == "Sticky note")) { return savedItemPose; } bool surfaceFound; FurnitureRelationship furnitureRelationship = FurnitureRelationship.FromItem(item, out surfaceFound); if (!surfaceFound) { return savedItemPose; } if (furnitureRelationship == null) { ItemPositionSkipped?.Invoke(item, "its furniture or parent could not be found"); return null; } if (ConfigManager.ExcludedUnlockables.Contains(furnitureRelationship.FurnitureKey)) { ItemPositionSkipped?.Invoke(item, $"its furniture {furnitureRelationship.FurnitureKey} is excluded in the config"); return null; } if (PlaceableObjectsSurfaceRegistry.TryGet(furnitureRelationship.Surface, out var registration) && !registration.AllowAutomaticSaving) { ItemPositionSkipped?.Invoke(item, registration.OwnerGuid + " disabled automatic saving for its surface through the API"); return null; } furniture = furnitureRelationship; savedItemPose.parentKind = ItemParentKind.Furniture; savedItemPose.furnitureKey = ((object)furnitureRelationship.FurnitureKey).ToString(); savedItemPose.furnitureParentPath = furnitureRelationship.ParentPath; savedItemPose.localPosition = furnitureRelationship.ItemParent.InverseTransformPoint(((Component)item).transform.position); savedItemPose.localRotation = Quaternion.Inverse(furnitureRelationship.ItemParent.rotation) * ((Component)item).transform.rotation; return savedItemPose; } private static void Forget(GrabbableObject item) { if (NetworkManager.Singleton.IsServer) { hostItems.Remove(((NetworkBehaviour)item).NetworkObjectId); PlaceableObjectsSurfaceRegistry.BasedItems.Remove(item); } } private static void UnlockablesSynced(StartOfRound round) { if (!((NetworkBehaviour)round).IsServer) { _unlockablesSynced = true; RequestAssignments(); } } private static void RequestAssignments() { if (NetworkManager.Singleton.IsListening && !HatNet.SendToServer(RequestKey, string.Empty)) { Log.Error("Current item data could not be requested from the server"); } } private static void AssignmentsRequested(ulong clientId, string data) { StartOfRound instance = StartOfRound.Instance; ((MonoBehaviour)instance).StartCoroutine(SendAssignments(instance, clientId)); } private static IEnumerator SendAssignments(StartOfRound round, ulong clientId) { while (Plugin.HasSelfSortingStorage && SelfSortingStorageCompat.Reloading) { yield return null; } NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)round == (Object)null || !((NetworkBehaviour)round).IsSpawned || !singleton.IsListening || !singleton.ConnectedClients.ContainsKey(clientId)) { yield break; } hostItems.Clear(); GrabbableObject[] array = (from networkObject in singleton.SpawnManager.SpawnedObjects.Values select ((Component)networkObject).GetComponent<GrabbableObject>() into item where (Object)(object)item != (Object)null && ((Component)item).gameObject.activeInHierarchy select item).ToArray(); GrabbableObject[] array2 = array; foreach (GrabbableObject val in array2) { if (ItemExtensions.GetDawnInfo(val.itemProperties) != null) { SaveItem(val, new JObject(), array); } } ItemAssignmentList itemAssignmentList = new ItemAssignmentList { items = hostItems.Values.Where((ItemAssignment assignment) => !assignment.valuesOnly).ToList(), variants = hostItems.Values.Where((ItemAssignment assignment) => assignment.valuesOnly).ToList() }; int num2 = hostItems.Values.Count((ItemAssignment assignment) => assignment.meshVariant >= 0); int num3 = hostItems.Values.Count((ItemAssignment assignment) => assignment.materialVariant >= 0); int num4 = hostItems.Values.Count((ItemAssignment assignment) => assignment.batteryCharge >= 0f); int num5 = hostItems.Values.Count((ItemAssignment assignment) => assignment.chemicalFuel >= 0f); int num6 = hostItems.Values.Count((ItemAssignment assignment) => assignment.sprayPaintTank >= 0f); if (HatNet.SendToClient(AssignmentKey, clientId, JsonUtility.ToJson((object)itemAssignmentList))) { Log.Debug($"Sent {itemAssignmentList.items.Count} item positions, {num2} mesh variants, {num3} material variants, {num4} battery charges, {num5} TZP fuel, and {num6} spray paint fuel to client {clientId}"); ItemsSynced?.Invoke(itemAssignmentList, clientId, arg3: true); } else { Log.Error($"Current item data could not be sent to client {clientId}"); } } private static void AssignmentsReceived(ulong clientId, string data) { if (NetworkManager.Singleton.IsServer || clientId != 0L) { return; } try { ItemAssignmentList itemAssignmentList = JsonUtility.FromJson<ItemAssignmentList>(data); StartOfRound instance = StartOfRound.Instance; if (itemAssignmentList?.items == null) { return; } ItemsSynced?.Invoke(itemAssignmentList, clientId, arg3: false); foreach (ItemAssignment item in itemAssignmentList.items) { if (item.pose.parentKind != ItemParentKind.BeltBag) { NetworkRefs.WhenReady<GrabbableObject>((MonoBehaviour)(object)instance, item.itemNetworkId, (Action<GrabbableObject>)delegate(GrabbableObject item) { PlaceableObjectsSurfaceRegistry.BasedItems.Add(item); }, 8f, 0); } } if (_unlockablesSynced) { PlaceClientItems(instance, itemAssignmentList); } } catch (ArgumentException) { Log.Error("Item data received from the server could not be read"); } } private static void PlaceClientItems(StartOfRound round, ItemAssignmentList assignments) { if (assignments.items.Count == 0 && assignments.variants.Count == 0) { return; } assignments.colliderCounts = new Dictionary<string, int>(); Dictionary<ItemAssignment, string> unplaced = new Dictionary<ItemAssignment, string>(); foreach (ItemAssignment assignment in assignments.variants) { NetworkRefs.WhenReady<GrabbableObject>((MonoBehaviour)(object)round, assignment.itemNetworkId, (Action<GrabbableObject>)delegate(GrabbableObject item) { RestoreItemValues(item, assignment, assignments); }, 8f, 4); } foreach (ItemAssignment assignment2 in assignments.items) { unplaced[assignment2] = "the item did not become ready for placement"; NetworkRefs.WhenReady<GrabbableObject>((MonoBehaviour)(object)round, assignment2.itemNetworkId, (Action<GrabbableObject>)delegate(GrabbableObject item) { RestoreItemValues(item, assignment2, assignments); }, 8f, 4); if (assignment2.pose.parentKind == ItemParentKind.BeltBag) { unplaced[assignment2] = "its belt bag could not be resolved"; NetworkRefs.WhenReady<BeltBagItem>((MonoBehaviour)(object)round, assignment2.parentNetworkId, (Action<BeltBagItem>)delegate(BeltBagItem beltBag) { unplaced[assignment2] = "the item did not become ready for placement"; NetworkRefs.WhenReady<GrabbableObject>((MonoBehaviour)(object)round, assignment2.itemNetworkId, (Action<GrabbableObject>)delegate(GrabbableObject item) { if (Plugin.HasSelfSortingStorage && SelfSortingStorageCompat.OwnsPosition(item)) { PlaceableObjectsSurfaceRegistry.BasedItems.Remove(item); unplaced[assignment2] = "SelfSortingStorage owns it"; } else if (item.isHeld || item.heldByPlayerOnServer || (Object)(object)item.playerHeldBy != (Object)null || item.isHeldByEnemy) { unplaced[assignment2] = "it was being held"; } else { if (!beltBag.objectsInBag.Contains(item)) { beltBag.PutObjectInBagLocalClient(item); } Utility.ItemRestored?.Invoke(item, assignment2.pose, null); assignments.placedCount++; unplaced.Remove(assignment2); } }, 8f, 4); }, 8f, 0); continue; } if (assignment2.pose.parentKind == ItemParentKind.Ship) { NetworkRefs.WhenReady<GrabbableObject>((MonoBehaviour)(object)round, assignment2.itemNetworkId, (Action<GrabbableObject>)delegate(GrabbableObject item) { if (Plugin.HasSelfSortingStorage && SelfSortingStorageCompat.OwnsPosition(item)) { PlaceableObjectsSurfaceRegistry.BasedItems.Remove(item); unplaced[assignment2] = "SelfSortingStorage owns it"; } else if (item.isHeld || item.heldByPlayerOnServer || (Object)(object)item.playerHeldBy != (Object)null || item.isHeldByEnemy) { unplaced[assignment2] = "it was being held"; } else if (Utility.PlaceShipPose(item, assignment2.pose, assignments)) { assignments.placedCount++; unplaced.Remove(assignment2); } else { unplaced[assignment2] = "the item was no longer available when placement ran"; } }, 8f, 4); continue; } if (assignment2.pose.parentKind == ItemParentKind.Cruiser) { unplaced[assignment2] = "its cruiser could not be resolved"; } else { unplaced[assignment2] = "its furniture could not be resolved"; } NetworkRefs.WhenReady((MonoBehaviour)(object)round, assignment2.parentNetworkId, (Action<NetworkObject>)delegate(NetworkObject root) { Transform parent; if (assignment2.pose.parentKind == ItemParentKind.Cruiser) { parent = ((Component)root).transform; } else { parent = Utility.FurnitureParentFromPath(((Component)root).transform, assignment2.pose.furnitureParentPath); } if ((Object)(object)parent == (Object)null) { unplaced[assignment2] = "its saved parent could not be found on " + ((Object)root).name; } else { unplaced[assignment2] = "the item did not become ready for placement"; NetworkRefs.WhenReady<GrabbableObject>((MonoBehaviour)(object)round, assignment2.itemNetworkId, (Action<GrabbableObject>)delegate(GrabbableObject item) { if (Plugin.HasSelfSortingStorage && SelfSortingStorageCompat.OwnsPosition(item)) { PlaceableObjectsSurfaceRegistry.BasedItems.Remove(item); unplaced[assignment2] = "SelfSortingStorage owns it"; } else if (item.isHeld || item.heldByPlayerOnServer || (Object)(object)item.playerHeldBy != (Object)null || item.isHeldByEnemy) { unplaced[assignment2] = "it was being held"; } else if (Utility.PlaceFurniturePose(item, parent, assignment2.pose, assignments)) { assignments.placedCount++; unplaced.Remove(assignment2); } else { unplaced[assignment2] = "the item was no longer available when placement ran"; } }, 8f, 4); } }, 8f, 0); } ((MonoBehaviour)round).StartCoroutine(ClientPlacementSummary(round, assignments, unplaced)); } private static IEnumerator ClientPlacementSummary(StartOfRound round, ItemAssignmentList assignments, Dictionary<ItemAssignment, string> unplaced) { yield return waitForSecondsRealtime1625; if ((Object)(object)round == (Object)null || !((NetworkBehaviour)round).IsSpawned) { yield break; } Log.Info($"Client restored {assignments.placedCount} of {assignments.items.Count} item positions, {assignments.meshVariantCount} mesh variants, {assignments.materialVariantCount} material variants, {assignments.batteryCount} battery charges, {assignments.chemicalCount} TZP fuel and {assignments.sprayPaintCount} spray paint fuel from server"); if (assignments.colliderCounts.Count > 0) { Log.Debug("Client restored " + string.Join(", ", assignments.colliderCounts.Select<KeyValuePair<string, int>, string>((KeyValuePair<string, int> entry) => $"{entry.Value} item positions inside {entry.Key}"))); } if (assignments.returnedToShipCount > 0) { Log.Debug($"Client returned {assignments.returnedToShipCount} items to the middle of the ship"); } foreach (var (itemAssignment2, text2) in unplaced) { Log.Debug("Client has not restored the saved position for " + itemAssignment2.itemName + " because " + text2); } } private static void RestoreItemValues(GrabbableObject item, ItemAssignment assignment, ItemAssignmentList assignments) { //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Expected O, but got Unknown Mesh[] meshVariants = item.itemProperties.meshVariants; Material[] materialVariants = item.itemProperties.materialVariants; if (assignment.meshVariant >= 0) { MeshFilter val = default(MeshFilter); if (meshVariants != null && assignment.meshVariant < meshVariants.Length && ((Component)item).TryGetComponent<MeshFilter>(ref val)) { val.sharedMesh = meshVariants[assignment.meshVariant]; assignments.meshVariantCount++; ItemValueRestored?.Invoke(item, "mesh variant", assignment.meshVariant); } else { Log.Warn($"Skipped unavailable mesh variant {assignment.meshVariant} for {item.itemProperties.itemName}"); } } if (assignment.materialVariant >= 0) { MeshRenderer val2 = default(MeshRenderer); if (materialVariants != null && assignment.materialVariant < materialVariants.Length && ((Component)item).TryGetComponent<MeshRenderer>(ref val2)) { ((Renderer)val2).sharedMaterial = materialVariants[assignment.materialVariant]; assignments.materialVariantCount++; ItemValueRestored?.Invoke(item, "material variant", assignment.materialVariant); } else { Log.Warn($"Skipped unavailable material variant {assignment.materialVariant} for {item.itemProperties.itemName}"); } } if (assignment.batteryCharge >= 0f) { item.insertedBattery = new Battery(assignment.batteryCharge <= 0f, assignment.batteryCharge); item.ChargeBatteries(); assignments.batteryCount++; ItemValueRestored?.Invoke(item, "battery charge", assignment.batteryCharge); } if (assignment.chemicalFuel >= 0f) { TetraChemicalItem val3 = (TetraChemicalItem)(object)((item is TetraChemicalItem) ? item : null); if (val3 != null) { val3.fuel = assignment.chemicalFuel; ((GrabbableObject)val3).itemUsedUp = assignment.chemicalFuel <= 0f; assignments.chemicalCount++; ItemValueRestored?.Invoke(item, "TZP fuel", assignment.chemicalFuel); } else { Log.Warn("Skipped saved TZP fuel for incompatible item " + item.itemProperties.itemName); } } if (assignment.sprayPaintTank >= 0f) { SprayPaintItem val4 = (SprayPaintItem)(object)((item is SprayPaintItem) ? item : null); if (val4 != null) { val4.sprayCanTank = assignment.sprayPaintTank; assignments.sprayPaintCount++; ItemValueRestored?.Invoke(item, "spray paint/weed killer fuel", assignment.sprayPaintTank); } else { Log.Warn("Skipped saved spray paint fuel for incompatible item " + item.itemProperties.itemName); } } } private static void RoundCleared(StartOfRound round) { if (_restoreCoroutine != null) { ((MonoBehaviour)round).StopCoroutine(_restoreCoroutine); _restoreCoroutine = null; } _unlockablesSynced = false; pending.Clear(); pendingItemValues.Clear(); loadedItems.Clear(); hostItems.Clear(); PlaceableObjectsSurfaceRegistry.IgnoredItems.Clear(); PlaceableObjectsSurfaceRegistry.BasedItems.Clear(); collectedItems.Clear(); } } internal enum ItemParentKind { Ship, Furniture, BeltBag, Cruiser } [Serializable] internal class SavedItemPose { public ItemParentKind parentKind; public Vector3 worldPosition; public Vector3 localPosition; public Quaternion worldRotation; public Quaternion localRotation; public string furnitureKey = string.Empty; public string furnitureParentPath = string.Empty; public string parentItemId = string.Empty; } [Serializable] internal class ItemAssignment { public string itemName; public ulong itemNetworkId; public ulong parentNetworkId; public int meshVariant = -1; public int materialVariant = -1; public float batteryCharge = -1f; public float chemicalFuel = -1f; public float sprayPaintTank = -1f; public SavedItemPose pose; internal bool valuesOnly; internal ItemAssignment(GrabbableObject item, SavedItemPose pose, FurnitureRelationship? furniture = null) { itemName = item.itemProperties.itemName; itemNetworkId = ((NetworkBehaviour)item).NetworkObjectId; if (furniture != null) { parentNetworkId = ((NetworkBehaviour)furniture.ShipObject.parentObject).NetworkObjectId; } this.pose = pose; } internal ItemAssignment(GrabbableObject item, int meshVariant, int materialVariant, float batteryCharge, float chemicalFuel, float sprayPaintTank) { itemName = item.itemProperties.itemName; itemNetworkId = ((NetworkBehaviour)item).NetworkObjectId; this.meshVariant = meshVariant; this.materialVariant = materialVariant; this.batteryCharge = batteryCharge; this.chemicalFuel = chemicalFuel; this.sprayPaintTank = sprayPaintTank; valuesOnly = true; } } [Serializable] internal class ItemAssignmentList { public List<ItemAssignment> items = new List<ItemAssignment>(); public List<ItemAssignment> variants = new List<ItemAssignment>(); internal Dictionary<string, int> colliderCounts = new Dictionary<string, int>(); internal int placedCount; internal int meshVariantCount; internal int materialVariantCount; internal int batteryCount; internal int chemicalCount; internal int sprayPaintCount; internal int returnedToShipCount; } } namespace BasedItemSaving.Compat { internal static class PocketRoomsLibCompat { [MethodImpl(MethodImplOptions.NoInlining)] internal static Collider? SavingColliderAt(Vector3 position) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) foreach (Collider roomBound in MelShipBoundsCheck.roomBounds) { if ((Object)(object)roomBound != (Object)null && roomBound.enabled && ((Component)roomBound).gameObject.activeInHierarchy) { Bounds bounds = roomBound.bounds; if (((Bounds)(ref bounds)).Contains(position)) { return roomBound; } } } return null; } } internal static class SelfSortingStorageCompat { private static Component _cupboard = null; private static readonly HashSet<IEnumerator> reloads = new HashSet<IEnumerator>(); internal static bool Reloading { [MethodImpl(MethodImplOptions.NoInlining)] get { if ((Object)(object)_cupboard != (Object)null) { return reloads.Count > 0; } return false; } } [MethodImpl(MethodImplOptions.NoInlining)] internal static bool OwnsPosition(GrabbableObject item) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown if ((Object)(object)_cupboard == (Object)null || item.isHeld || item.heldByPlayerOnServer || (Object)(object)item.playerHeldBy != (Object)null || item.isHeldByEnemy) { return false; } SmartCupboard val = (SmartCupboard)_cupboard; if (val.placedItems.ContainsValue(item)) { return true; } Transform parent = ((Component)item).transform.parent; Transform[] placePositions = val.placePositions; foreach (Transform val2 in placePositions) { if ((Object)(object)val2 != (Object)null && (Object)(object)parent == (Object)(object)val2) { return true; } } return false; } [HarmonyPatch(typeof(SmartCupboard), "OnNetworkSpawn")] [HarmonyPostfix] private static void OnNetworkSpawnPostfix(SmartCupboard __instance) { _cupboard = (Component)(object)__instance; reloads.Clear(); } [HarmonyPatch(typeof(SmartCupboard), "OnNetworkDespawn")] [HarmonyPostfix] private static void OnNetworkDespawnPostfix(SmartCupboard __instance) { if (!((Object)(object)_cupboard != (Object)(object)__instance)) { _cupboard = null; reloads.Clear(); } } [HarmonyPatch(typeof(SmartCupboard), "ReloadPlacedItems")] [HarmonyPostfix] private static IEnumerator ReloadPlacedItemsPostfix(IEnumerator values) { reloads.Add(values); try { while (values.MoveNext()) { yield return values.Current; } } finally { reloads.Remove(values); if (values is IDisposable disposable) { disposable.Dispose(); } } } } } namespace BasedItemSaving.API { public readonly struct PlaceableObjectsSurfaceRegistrationAPI { public string OwnerGuid { get; } public Type SurfaceType { get; } public bool AllowAutomaticSaving { get; } public PlaceableObjectsSurfaceRegistrationAPI(string ownerGuid, Type surfaceType, bool allowAutomaticSaving) { OwnerGuid = ownerGuid; SurfaceType = surfaceType; AllowAutomaticSaving = allowAutomaticSaving; } } public static class PlaceableObjectsSurfaceRegistry { private static readonly List<PlaceableObjectsSurfaceRegistrationAPI> registrations = new List<PlaceableObjectsSurfaceRegistrationAPI>(); internal static readonly HashSet<GrabbableObject> IgnoredItems = new HashSet<GrabbableObject>(); internal static readonly HashSet<GrabbableObject> BasedItems = new HashSet<GrabbableObject>(); public static void Register<TSurface>(string ownerGuid, bool allowAutomaticSaving) where TSurface : PlaceableObjectsSurface { Type surfaceType = typeof(TSurface); if (!registrations.Any((PlaceableObjectsSurfaceRegistrationAPI entry) => entry.OwnerGuid == ownerGuid && entry.SurfaceType == surfaceType)) { registrations.Add(new PlaceableObjectsSurfaceRegistrationAPI(ownerGuid, surfaceType, allowAutomaticSaving)); } } public static bool TryGet(PlaceableObjectsSurface surface, out PlaceableObjectsSurfaceRegistrationAPI registration) { registration = registrations.FirstOrDefault((PlaceableObjectsSurfaceRegistrationAPI entry) => entry.SurfaceType.IsInstanceOfType(surface)); return registration.SurfaceType != null; } public static void Ignore(GrabbableObject item) { IgnoredItems.Add(item); } public static void Allow(GrabbableObject item) { IgnoredItems.Remove(item); } public static bool IsIgnored(GrabbableObject item) { if ((Object)(object)item != (Object)null) { if (!IgnoredItems.Contains(item)) { return BasedItems.Contains(item); } return true; } return false; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }