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 v0.1.4
BasedItemSaving.dll
Decompiled 3 days 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.Saving; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Dawn; using Dawn.Internal; using HatLib; using HatLib.Tools; using Microsoft.CodeAnalysis; using Newtonsoft.Json.Linq; using Unity.Netcode; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("com.github.teamxiaolan.dawnlib")] [assembly: IgnoresAccessChecksTo("com.github.teamxiaolan.dawnlib.dusk")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("BasedItemSaving")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.4.0")] [assembly: AssemblyInformationalVersion("0.1.4")] [assembly: AssemblyProduct("BasedItemSaving")] [assembly: AssemblyTitle("BasedItemSaving")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.4.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BasedItemSaving { internal static class ConfigManager { private static readonly HashSet<NamespacedKey<DawnUnlockableItemInfo>> excludedUnlockables = new HashSet<NamespacedKey<DawnUnlockableItemInfo>>(); internal static void Bind(ConfigFile config) { ConfigEntry<string> exclusions = config.Bind<string>("Item Saving", "Excluded Unlockables", "misideitems:houseteleporter,melaniepocketroom:melmstockroomzero, melaniepocketroom:melmbedroomzero, melaniepocketroom:melmbedroomone, unknown_lib:basement, unknown_lib:house, unknown_lib:poolroom, unknown_lib:garden, melaniecrafting:workbench, melaniecrafting:crucible, melaniemeliciouscooked:grinder, melaniemeliciouscooked:blender, melaniemeliciouscooked:crockpot, melaniemeliciouscooked:toasteroven, melaniemeliciouscooked:portablegrill, melaniemeliciouscooked:oven, melaniemeliciouscooked:minifridge, melaniemeliciouscooked:plantera, melaniemeliciouscooked:planterb, melaniemeliciouscooked:planterc, melaniemeliciouscooked:trellisa, melaniemeliciouscooked:trellisb, melaniemeliciouscooked:plantershelfa, melaniemeliciouscooked:barrela, melaniemeliciouscooked:barrelb, melaniemeliciouscooked:wineracka, melaniemeliciouscooked:beeboxa, melaniemeliciouscooked:pota, melaniemeliciouscooked:icecreammaker, melaniemeliciouscooked:seedmaker", "Unlockables that should be ignored and excluded from item reparenting"); ReadExclusions(exclusions.Value); exclusions.SettingChanged += delegate { ReadExclusions(exclusions.Value); }; } internal static bool IsExcluded(NamespacedKey<DawnUnlockableItemInfo> key) { return excludedUnlockables.Contains(key); } private static void ReadExclusions(string value) { excludedUnlockables.Clear(); string[] array = value.Split(',', StringSplitOptions.RemoveEmptyEntries); NamespacedKey val = default(NamespacedKey); foreach (string text in array) { string text2 = text.Trim(); if (NamespacedKey.TryParse(text2, ref val)) { excludedUnlockables.Add(val.AsTyped<DawnUnlockableItemInfo>()); } else { Log.Error("Excluded unlockable key " + text2 + " could not be read"); } } } } internal static class Log { internal static void Debug(string message) { Write((LogLevel)32, message); } internal static void Info(string message) { Write((LogLevel)16, message); } internal static void Warn(string message) { Write((LogLevel)4, message); } internal static void Error(string message) { Write((LogLevel)2, message); } internal static void Fatal(string message) { Write((LogLevel)1, message); } private static void Write(LogLevel logLevel, string message) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) Plugin.mls.Log(logLevel, (object)message); } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("MrHat.BasedItemSaving", "BasedItemSaving", "0.1.4")] internal class Plugin : BaseUnityPlugin { public const string modGUID = "MrHat.BasedItemSaving"; private const string modName = "BasedItemSaving"; private const string modVersion = "0.1.4"; internal static ManualLogSource mls; private void Awake() { mls = Logger.CreateLogSource("MrHat.BasedItemSaving"); ShipItemSaving.Init(); mls.LogInfo((object)"BasedItemSaving 0.1.4 loaded"); } } internal static class Utility { internal static string? FurnitureParentPath(Transform root, Transform parent) { if ((Object)(object)parent == (Object)(object)root) { return string.Empty; } List<int> list = new List<int>(); Transform val = parent; while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root) { list.Add(val.GetSiblingIndex()); val = val.parent; } if ((Object)(object)val != (Object)(object)root) { return null; } list.Reverse(); return string.Join("/", list); } internal static Transform? FurnitureParentFromPath(Transform root, string path) { Transform val = root; if (string.IsNullOrWhiteSpace(path)) { return val; } string[] array = path.Split('/'); foreach (string s in array) { if (!int.TryParse(s, out var result) || result < 0 || result >= val.childCount) { return null; } val = val.GetChild(result); } return val; } internal static bool PlaceFurniturePose(GrabbableObject? item, Transform parent, Vector3 position, Quaternion rotation) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)item == (Object)null || !GrabbablePlacement.Place(item, parent, parent.TransformPoint(position), parent.rotation * rotation)) { return false; } ((Component)item).transform.SetLocalPositionAndRotation(position, rotation); item.targetFloorPosition = position; item.startFallingPosition = position + Vector3.up * 0.0001f; return true; } } public static class MyPluginInfo { public const string PLUGIN_GUID = "BasedItemSaving"; public const string PLUGIN_NAME = "BasedItemSaving"; public const string PLUGIN_VERSION = "0.1.4"; } } namespace BasedItemSaving.Saving { internal class FurnitureRelationship(PlaceableObjectsSurface surface, PlaceableShipObject shipObject, Transform itemParent, NamespacedKey<DawnUnlockableItemInfo> furnitureKey, string parentPath) { internal PlaceableObjectsSurface Surface { get; } = surface; internal PlaceableShipObject ShipObject { get; } = shipObject; internal Transform ItemParent { get; } = itemParent; internal NamespacedKey<DawnUnlockableItemInfo> FurnitureKey { get; } = furnitureKey; internal string ParentPath { get; } = parentPath; internal static FurnitureRelationship? FromItem(GrabbableObject item, out bool surfaceFound) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) surfaceFound = false; Transform parent = ((Component)item).transform.parent; PlaceableObjectsSurface val = null; if ((Object)(object)parent != (Object)null) { val = SurfaceAt(parent, item, ((Component)item).transform.position, Object.FindObjectsByType<PlaceableObjectsSurface>((FindObjectsSortMode)0), out surfaceFound); } PlaceableShipObject val2 = (((Object)(object)val == (Object)null || (Object)(object)val.parentTo == (Object)null) ? null : ((Component)val.parentTo).GetComponentInChildren<PlaceableShipObject>(true)); StartOfRound instance = StartOfRound.Instance; if ((Object)(object)val2 == (Object)null || (Object)(object)val2.parentObject == (Object)null) { return null; } int unlockableID = val2.unlockableID; UnlockableItem val3 = ((unlockableID >= 0 && unlockableID < instance.unlockablesList.unlockables.Count) ? instance.unlockablesList.unlockables[unlockableID] : null); DawnUnlockableItemInfo val4 = ((val3 != null) ? UnlockableItemExtensions.GetDawnInfo(val3) : null); string text = Utility.FurnitureParentPath(((Component)val2.parentObject).transform, parent); if (val3 != null && val4 != null && !val3.inStorage && text != null) { return new FurnitureRelationship(val, val2, parent, ((DawnBaseInfo<DawnUnlockableItemInfo>)(object)val4).TypedKey, text); } return null; } internal static bool RestoreParent(StartOfRound round, GrabbableObject item, SavedItemPose pose, out PlaceableShipObject? shipObject, out Transform? parent, out bool unresolved) { //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) shipObject = null; parent = null; unresolved = false; NamespacedKey val = default(NamespacedKey); if (!NamespacedKey.TryParse(pose.furnitureKey, ref val)) { unresolved = true; return false; } NamespacedKey<DawnUnlockableItemInfo> key = val.AsTyped<DawnUnlockableItemInfo>(); if (ConfigManager.IsExcluded(key)) { return false; } shipObject = ((IEnumerable<PlaceableShipObject>)Object.FindObjectsByType<PlaceableShipObject>((FindObjectsSortMode)0)).FirstOrDefault((Func<PlaceableShipObject, bool>)delegate(PlaceableShipObject candidate) { int unlockableID = candidate.unlockableID; if (unlockableID >= 0 && unlockableID < round.unlockablesList.unlockables.Count) { UnlockableItem obj = round.unlockablesList.unlockables[unlockableID]; if (obj == null) { return false; } return ((object)((DawnBaseInfo<DawnUnlockableItemInfo>)(object)UnlockableItemExtensions.GetDawnInfo(obj))?.TypedKey).Equals((object?)key) == true; } return false; }); if ((Object)(object)shipObject == (Object)null || (Object)(object)shipObject.parentObject == (Object)null || !((NetworkBehaviour)shipObject.parentObject).IsSpawned || round.unlockablesList.unlockables[shipObject.unlockableID].inStorage || (Object)(object)(parent = Utility.FurnitureParentFromPath(((Component)shipObject.parentObject).transform, pose.furnitureParentPath)) == (Object)null) { unresolved = true; return false; } bool found; PlaceableObjectsSurface val2 = SurfaceAt(parent, item, parent.TransformPoint(pose.localPosition), ((Component)shipObject.parentObject).GetComponentsInChildren<PlaceableObjectsSurface>(true), out found); if (!((Object)(object)val2 == (Object)null) && PlaceableObjectsSurfaceRegistry.TryGet(val2, out var registration)) { return registration.AllowAutomaticSaving; } return true; } private static PlaceableObjectsSurface? SurfaceAt(Transform parent, GrabbableObject item, Vector3 position, PlaceableObjectsSurface[] surfaces, out bool found) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) PlaceableObjectsSurface[] array = surfaces.Where((PlaceableObjectsSurface surface) => (Object)(object)ParentFor(surface) == (Object)(object)parent).ToArray(); found = array.Length != 0; if (array.Length < 2) { return array.FirstOrDefault(); } float offset = 0f; if ((Object)(object)item.itemProperties != (Object)null) { offset = item.itemProperties.verticalOffset; } return array.Where((PlaceableObjectsSurface surface) => (Object)(object)surface.placeableBounds != (Object)null).OrderBy(delegate(PlaceableObjectsSurface surface) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) Collider placeableBounds = surface.placeableBounds; Vector3 val = position - ((Component)placeableBounds).transform.up * offset; Vector3 val2 = placeableBounds.ClosestPoint(val) - val; return ((Vector3)(ref val2)).sqrMagnitude; }).FirstOrDefault(); } private static Transform? ParentFor(PlaceableObjectsSurface surface) { Transform val = null; if ((Object)(object)surface.parentTo != (Object)null) { val = ((Component)surface.parentTo).transform; } PlayerPhysicsRegion val2 = null; if ((Object)(object)val != (Object)null) { val2 = ((IEnumerable<PlayerPhysicsRegion>)((Component)val).GetComponentsInChildren<PlayerPhysicsRegion>()).FirstOrDefault((Func<PlayerPhysicsRegion, bool>)((PlayerPhysicsRegion candidate) => ((Behaviour)candidate).isActiveAndEnabled)); } if (!((Object)(object)val2 != (Object)null) || !val2.allowDroppingItems || !((Object)(object)val2.physicsTransform != (Object)null)) { return val; } return val2.physicsTransform; } } internal static class ShipItemSaving { [CompilerGenerated] private static class <>O { public static Action<GrabbableObject, JObject> <0>__SaveItem; public static Action<GrabbableObject, JObject> <1>__ReadItem; public static Action<StartOfRound> <2>__RoundStarted; public static Action<StartOfRound> <3>__ItemsLoaded; public static Action<StartOfRound> <4>__UnlockablesSynced; public static Action <5>__GameSaved; public static Action<StartOfRound> <6>__RoundCleared; public static HatNetMessageHandler <7>__AssignmentsReceived; public static HatNetMessageHandler <8>__AssignmentsRequested; public static Action<GrabbableObject> <9>__MarkBasedItem; } private static readonly WaitForSeconds _waitForSeconds0_5 = new WaitForSeconds(0.5f); private const float NetworkWaitSeconds = 8f; private static readonly NamespacedKey PoseKey = NamespacedKey.From("baseditemsaving", "exact_pose"); private static readonly NamespacedKey ItemIdKey = NamespacedKey.From("baseditemsaving", "item_id"); private static readonly NamespacedKey MeshVariantKey = NamespacedKey.From("baseditemsaving", "mesh_variant"); private static readonly NamespacedKey MaterialVariantKey = NamespacedKey.From("baseditemsaving", "material_variant"); private static readonly NamespacedKey BatteryChargeKey = NamespacedKey.From("baseditemsaving", "battery_charge"); private static readonly NamespacedKey ChemicalFuelKey = NamespacedKey.From("baseditemsaving", "chemical_fuel"); private static readonly NamespacedKey SprayPaintTankKey = NamespacedKey.From("baseditemsaving", "spray_paint_tank"); private static readonly NamespacedKey AssignmentKey = NamespacedKey.From("baseditemsaving", "item_assignments"); private static readonly NamespacedKey RequestKey = NamespacedKey.From("baseditemsaving", "item_assignments_request"); private static readonly Dictionary<GrabbableObject, SavedItemPose> pending = new Dictionary<GrabbableObject, SavedItemPose>(); private static readonly Dictionary<GrabbableObject, (int Mesh, int Material, float Battery, float Chemical, float SprayPaint)> pendingItemValues = new Dictionary<GrabbableObject, (int, int, float, float, float)>(); private static readonly Dictionary<string, GrabbableObject> loadedItems = new Dictionary<string, GrabbableObject>(); private static readonly Dictionary<ulong, ItemAssignment> hostItems = new Dictionary<ulong, ItemAssignment>(); private static readonly HashSet<Item> missingVariantDataLogged = new HashSet<Item>(); private static bool unlockablesSynced; private static int savedPositions; private static int savedMeshVariants; private static int savedMaterialVariants; private static int savedBatteries; private static int savedChemicals; private static int savedSprayPaint; internal static void Init() { //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Expected O, but got Unknown //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Expected O, but got Unknown DawnItemSaveEvents.OnCollectExtraSaveData += SaveItem; DawnItemSaveEvents.OnLoadExtraSaveData += ReadItem; RoundEvents.OnRoundStart += RoundStarted; RoundEvents.OnShipItemsLoaded += ItemsLoaded; RoundEvents.OnShipUnlockablesSynced += UnlockablesSynced; RoundEvents.OnGameSaved += GameSaved; RoundEvents.OnLocalDisconnect += RoundCleared; RoundEvents.OnDestroy += RoundCleared; NamespacedKey assignmentKey = AssignmentKey; object obj = <>O.<7>__AssignmentsReceived; if (obj == null) { HatNetMessageHandler val = AssignmentsReceived; <>O.<7>__AssignmentsReceived = val; obj = (object)val; } HatNet.AddClientHandler(assignmentKey, (HatNetMessageHandler)obj); NamespacedKey requestKey = RequestKey; object obj2 = <>O.<8>__AssignmentsRequested; if (obj2 == null) { HatNetMessageHandler val2 = AssignmentsRequested; <>O.<8>__AssignmentsRequested = val2; obj2 = (object)val2; } HatNet.AddServerHandler(requestKey, (HatNetMessageHandler)obj2); } private static void SaveItem(GrabbableObject item, JObject extraData) { extraData[((object)ItemIdKey).ToString()] = JToken.op_Implicit(((NetworkBehaviour)item).NetworkObjectId.ToString()); WarnAboutMissingVariantData(item); Mesh[] meshVariants = item.itemProperties.meshVariants; Material[] materialVariants = item.itemProperties.materialVariants; int num = -1; MeshFilter val = default(MeshFilter); if (meshVariants != null && ((Component)item).TryGetComponent<MeshFilter>(ref val)) { num = Array.IndexOf(meshVariants, val.sharedMesh); if (num >= 0) { extraData[((object)MeshVariantKey).ToString()] = JToken.op_Implicit(num); savedMeshVariants++; } } int num2 = -1; MeshRenderer val2 = default(MeshRenderer); if (materialVariants != null && ((Component)item).TryGetComponent<MeshRenderer>(ref val2)) { num2 = Array.IndexOf(materialVariants, ((Renderer)val2).sharedMaterial); if (num2 >= 0) { extraData[((object)MaterialVariantKey).ToString()] = JToken.op_Implicit(num2); savedMaterialVariants++; } } float num3 = -1f; if (item.itemProperties.requiresBattery && item.insertedBattery != null) { num3 = Mathf.Clamp01(item.insertedBattery.charge); extraData[((object)BatteryChargeKey).ToString()] = JToken.op_Implicit(num3); savedBatteries++; } float num4 = -1f; GrabbableObject obj = item; TetraChemicalItem val3 = (TetraChemicalItem)(object)((obj is TetraChemicalItem) ? obj : null); if (val3 != null) { num4 = Mathf.Clamp01(val3.fuel); extraData[((object)ChemicalFuelKey).ToString()] = JToken.op_Implicit(num4); savedChemicals++; } float num5 = -1f; GrabbableObject obj2 = item; SprayPaintItem val4 = (SprayPaintItem)(object)((obj2 is SprayPaintItem) ? obj2 : null); if (val4 != null) { num5 = Mathf.Clamp01(val4.sprayCanTank); extraData[((object)SprayPaintTankKey).ToString()] = JToken.op_Implicit(num5); savedSprayPaint++; } BeltBagItem val5 = ((IEnumerable<BeltBagItem>)Object.FindObjectsByType<BeltBagItem>((FindObjectsSortMode)0)).FirstOrDefault((Func<BeltBagItem, bool>)((BeltBagItem candidate) => candidate.objectsInBag.Contains(item))); if ((Object)(object)val5 != (Object)null) { SavedItemPose savedItemPose = new SavedItemPose { parentKind = ItemParentKind.BeltBag, parentItemId = ((NetworkBehaviour)val5).NetworkObjectId.ToString() }; extraData[((object)PoseKey).ToString()] = (JToken)(object)JObject.Parse(JsonUtility.ToJson((object)savedItemPose)); savedPositions++; Forget(item); NetworkManager singleton = NetworkManager.Singleton; NetworkObject networkObject = ((NetworkBehaviour)item).NetworkObject; NetworkObject networkObject2 = ((NetworkBehaviour)val5).NetworkObject; if (singleton.IsServer && networkObject.IsSpawned && networkObject2.IsSpawned) { hostItems[((NetworkBehaviour)item).NetworkObjectId] = new ItemAssignment(item, savedItemPose) { ParentNetworkId = ((NetworkBehaviour)val5).NetworkObjectId, MeshVariant = num, MaterialVariant = num2, BatteryCharge = num3, ChemicalFuel = num4, SprayPaintTank = num5 }; } return; } FurnitureRelationship furniture; SavedItemPose savedItemPose2 = PoseFor(item, out furniture); if (savedItemPose2 == null) { extraData.Remove(((object)PoseKey).ToString()); Forget(item); if (NetworkManager.Singleton.IsServer && ((NetworkBehaviour)item).NetworkObject.IsSpawned && (num >= 0 || num2 >= 0 || num3 >= 0f || num4 >= 0f || num5 >= 0f)) { hostItems[((NetworkBehaviour)item).NetworkObjectId] = new ItemAssignment(item, num, num2, num3, num4, num5); } return; } extraData[((object)PoseKey).ToString()] = (JToken)(object)JObject.Parse(JsonUtility.ToJson((object)savedItemPose2)); savedPositions++; NetworkManager singleton2 = NetworkManager.Singleton; NetworkObject networkObject3 = ((NetworkBehaviour)item).NetworkObject; if (singleton2.IsServer && networkObject3.IsSpawned) { hostItems[((NetworkBehaviour)item).NetworkObjectId] = new ItemAssignment(item, savedItemPose2, furniture) { MeshVariant = num, MaterialVariant = num2, BatteryCharge = num3, ChemicalFuel = num4, SprayPaintTank = num5 }; PlaceableObjectsSurfaceRegistry.MarkBasedItem(item); } } private static void ReadItem(GrabbableObject item, JObject extraData) { WarnAboutMissingVariantData(item); Mesh[] meshVariants = item.itemProperties.meshVariants; Material[] materialVariants = item.itemProperties.materialVariants; int item2 = ((meshVariants == null) ? (-1) : ReadVariantIndex(item, extraData, MeshVariantKey, meshVariants.Length, "mesh")); int item3 = ((materialVariants == null) ? (-1) : ReadVariantIndex(item, extraData, MaterialVariantKey, materialVariants.Length, "material")); float item4 = ReadAmount(item, extraData, BatteryChargeKey, "battery charge"); float item5 = ((item is TetraChemicalItem) ? ReadAmount(item, extraData, ChemicalFuelKey, "TZP fuel") : (-1f)); float item6 = ((item is SprayPaintItem) ? ReadAmount(item, extraData, SprayPaintTankKey, "spray paint") : (-1f)); NetworkManager singleton = NetworkManager.Singleton; if (!singleton.IsServer) { return; } pendingItemValues[item] = (item2, item3, item4, item5, item6); JToken obj = extraData[((object)ItemIdKey).ToString()]; string text = ((obj != null) ? obj.ToObject<string>() : null); if (!string.IsNullOrWhiteSpace(text)) { loadedItems[text] = item; } JToken val = extraData[((object)PoseKey).ToString()]; if (val == null) { return; } try { SavedItemPose savedItemPose = JsonUtility.FromJson<SavedItemPose>(((object)val).ToString()); bool flag = savedItemPose == null; bool flag2 = flag; if (!flag2) { ItemParentKind parentKind = savedItemPose.parentKind; bool flag3 = ((parentKind < ItemParentKind.Ship || parentKind > ItemParentKind.BeltBag) ? true : false); flag2 = flag3; } if (!flag2) { pending[item] = savedItemPose; if (savedItemPose.parentKind != ItemParentKind.BeltBag) { PlaceableObjectsSurfaceRegistry.MarkBasedItem(item); } } } catch (ArgumentException) { Log.Warn("Saved item position could not be read"); } } private static void WarnAboutMissingVariantData(GrabbableObject item) { bool flag = item.itemProperties.meshVariants == null; bool flag2 = item.itemProperties.materialVariants == null; if ((flag || flag2) && missingVariantDataLogged.Add(item.itemProperties)) { string text = ((flag && flag2) ? "mesh and material" : (flag ? "mesh" : "material")); Log.Warn(item.itemProperties.itemName + " has no " + text + " variant data. The affected variants will not be saved or restored."); } } private static int ReadVariantIndex(GrabbableObject item, JObject extraData, NamespacedKey key, int variantCount, string kind) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 JToken val = extraData[((object)key).ToString()]; if (val == null) { return -1; } if ((int)val.Type != 6) { Log.Warn(item.itemProperties.itemName + " has invalid saved " + kind + " variant data"); return -1; } int num = val.ToObject<int>(); if (num < 0 || num >= variantCount) { Log.Warn($"{item.itemProperties.itemName} has invalid saved {kind} variant index {num}"); return -1; } return num; } private static float ReadAmount(GrabbableObject item, JObject extraData, NamespacedKey key, string kind) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Invalid comparison between Unknown and I4 //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 JToken val = extraData[((object)key).ToString()]; if (val == null) { return -1f; } JTokenType type = val.Type; if ((int)type != 7 && (int)type != 6) { Log.Warn(item.itemProperties.itemName + " has invalid saved " + kind + " data"); return -1f; } float num = val.ToObject<float>(); if (num < 0f || num > 1f) { Log.Warn($"{item.itemProperties.itemName} has invalid saved {kind} value {num}"); return -1f; } return num; } private static void ItemsLoaded(StartOfRound round) { if (((NetworkBehaviour)round).IsServer) { ((MonoBehaviour)round).StartCoroutine(RestoreHost(round)); } } private static void RoundStarted(StartOfRound round) { unlockablesSynced = false; if (!((NetworkBehaviour)round).IsServer) { RequestAssignments(); } } private static void GameSaved() { if (NetworkManager.Singleton.IsHost) { LogSavedItems(); } } private static IEnumerator RestoreHost(StartOfRound round) { yield return _waitForSeconds0_5; ItemAssignmentList restored = new ItemAssignmentList(); int unresolvedCount = 0; Dictionary<GrabbableObject, Animator[]> animators = pending.Keys.ToDictionary((GrabbableObject item) => item, (GrabbableObject item) => ((Component)item).GetComponentsInChildren<Animator>()); Dictionary<GrabbableObject, float> animationFinishedAt = new Dictionary<GrabbableObject, float>(); HashSet<GrabbableObject> processed = new HashSet<GrabbableObject>(); GrabbableObject key; while (processed.Count < pending.Count) { foreach (KeyValuePair<GrabbableObject, SavedItemPose> item in pending) { item.Deconstruct(out key, out var value); GrabbableObject val = key; SavedItemPose savedItemPose = value; if (processed.Contains(val)) { continue; } Animator[] array = animators[val]; if (array.Length != 0) { if (!animationFinishedAt.TryGetValue(val, out var value2)) { if (!array.All(delegate(Animator animator) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)animator) && ((Behaviour)animator).isActiveAndEnabled && !Mathf.Approximately(animator.speed, 0f)) { AnimatorStateInfo currentAnimatorStateInfo = animator.GetCurrentAnimatorStateInfo(0); return ((AnimatorStateInfo)(ref currentAnimatorStateInfo)).normalizedTime >= 1f; } return true; })) { continue; } value2 = (animationFinishedAt[val] = Time.time + 0.1f); } if (Time.time < value2) { continue; } } processed.Add(val); bool unresolved = false; ItemAssignment itemAssignment; if (savedItemPose.parentKind == ItemParentKind.BeltBag) { if (loadedItems.TryGetValue(savedItemPose.parentItemId, out GrabbableObject value3)) { BeltBagItem val2 = (BeltBagItem)(object)((value3 is BeltBagItem) ? value3 : null); if (val2 != null && !((Object)(object)val == (Object)(object)val2)) { itemAssignment = new ItemAssignment(val, savedItemPose) { ParentNetworkId = ((NetworkBehaviour)val2).NetworkObjectId }; if (!val2.objectsInBag.Contains(val)) { val.heldByPlayerOnServer = true; val2.PutObjectInBagLocalClient(val); } goto IL_023d; } } itemAssignment = null; } else { itemAssignment = ((savedItemPose.parentKind != ItemParentKind.Ship) ? RestoreFurniture(round, val, savedItemPose, out unresolved) : ((!PlaceableObjectsSurfaceRegistry.IsExplicitlyIgnored(val) && GrabbablePlacement.Place(val, round.elevatorTransform, savedItemPose.worldPosition, savedItemPose.worldRotation)) ? new ItemAssignment(val, savedItemPose) : null)); } goto IL_023d; IL_023d: if (itemAssignment == null) { if (savedItemPose.parentKind == ItemParentKind.Furniture && unresolved) { unresolvedCount++; } Forget(val); continue; } if (pendingItemValues.TryGetValue(val, out (int, int, float, float, float) value4)) { (itemAssignment.MeshVariant, itemAssignment.MaterialVariant, itemAssignment.BatteryCharge, itemAssignment.ChemicalFuel, itemAssignment.SprayPaintTank) = value4; pendingItemValues.Remove(val); } RestoreItemValues(val, itemAssignment, restored); hostItems[((NetworkBehaviour)val).NetworkObjectId] = itemAssignment; if (savedItemPose.parentKind != ItemParentKind.BeltBag) { PlaceableObjectsSurfaceRegistry.MarkBasedItem(val); } restored.Items.Add(itemAssignment); if (array.Length != 0) { Log.Debug("Restored animated item " + val.itemProperties.itemName + " position"); } } if (processed.Count < pending.Count) { yield return null; } } foreach (KeyValuePair<GrabbableObject, (int, int, float, float, float)> pendingItemValue in pendingItemValues) { pendingItemValue.Deconstruct(out key, out var value5); GrabbableObject val3 = key; (int, int, float, float, float) tuple2 = value5; if (tuple2.Item1 >= 0 || tuple2.Item2 >= 0 || !(tuple2.Item3 < 0f) || !(tuple2.Item4 < 0f) || !(tuple2.Item5 < 0f)) { ItemAssignment itemAssignment2 = new ItemAssignment(val3, tuple2.Item1, tuple2.Item2, tuple2.Item3, tuple2.Item4, tuple2.Item5); RestoreItemValues(val3, itemAssignment2, restored); hostItems[((NetworkBehaviour)val3).NetworkObjectId] = itemAssignment2; restored.Variants.Add(itemAssignment2); } } if (unresolvedCount > 0) { Log.Warn($"{unresolvedCount} item positions could not be restored and were left unchanged"); } Log.Info($"Restored {restored.Items.Count} item positions, {restored.MeshVariantCount} mesh variants, {restored.MaterialVariantCount} material variants, {restored.BatteryCount} battery charges, {restored.ChemicalCount} TZP fuel and {restored.SprayPaintCount} spray paint fuel"); if (!HatNet.SendToClients(AssignmentKey, JsonUtility.ToJson((object)restored))) { Log.Warn("Restored item data could not be sent to clients"); } pending.Clear(); pendingItemValues.Clear(); loadedItems.Clear(); } private static ItemAssignment? RestoreFurniture(StartOfRound round, GrabbableObject item, SavedItemPose pose, out bool unresolved) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) if (PlaceableObjectsSurfaceRegistry.IsExplicitlyIgnored(item)) { unresolved = false; return null; } if (!FurnitureRelationship.RestoreParent(round, item, pose, out PlaceableShipObject shipObject, out Transform parent, out unresolved) || !Utility.PlaceFurniturePose(item, parent, pose.localPosition, pose.localRotation)) { return null; } return new ItemAssignment(item, pose) { ParentNetworkId = ((NetworkBehaviour)shipObject.parentObject).NetworkObjectId }; } private static SavedItemPose? PoseFor(GrabbableObject item, out FurnitureRelationship? furniture) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) furniture = null; if ((Object)(object)item == (Object)null || PlaceableObjectsSurfaceRegistry.IsExplicitlyIgnored(item)) { return null; } bool flag = item.heldByPlayerOnServer || (Object)(object)item.playerHeldBy != (Object)null || (item.isHeld && !item.isHeldByEnemy); Vector3 worldPosition = (flag ? item.GetItemFloorPosition(default(Vector3)) : ((Component)item).transform.position); Quaternion worldRotation = ((Component)item).transform.rotation; if (flag) { Vector3 restingRotation = item.itemProperties.restingRotation; float num = (((Object)(object)item.playerHeldBy != (Object)null) ? ((float)((int)((Component)item.playerHeldBy).transform.localEulerAngles.y + item.itemProperties.floorYOffset) + 90f) : ((Component)item).transform.eulerAngles.y); worldRotation = Quaternion.Euler(restingRotation.x, num, restingRotation.z); Log.Debug("Saved held " + item.itemProperties.itemName + " at resting position on floor"); } SavedItemPose savedItemPose = new SavedItemPose { parentKind = ItemParentKind.Ship, worldPosition = worldPosition, worldRotation = worldRotation }; if (item is ClipboardItem || (item is PhysicsProp && (Object)(object)item.itemProperties != (Object)null && item.itemProperties.itemName == "Sticky note")) { return savedItemPose; } bool surfaceFound; FurnitureRelationship furnitureRelationship = FurnitureRelationship.FromItem(item, out surfaceFound); if (!surfaceFound) { return savedItemPose; } if (furnitureRelationship == null || ConfigManager.IsExcluded(furnitureRelationship.FurnitureKey) || (PlaceableObjectsSurfaceRegistry.TryGet(furnitureRelationship.Surface, out var registration) && !registration.AllowAutomaticSaving)) { return null; } furniture = furnitureRelationship; savedItemPose.parentKind = ItemParentKind.Furniture; savedItemPose.furnitureKey = ((object)furnitureRelationship.FurnitureKey).ToString(); savedItemPose.furnitureParentPath = furnitureRelationship.ParentPath; savedItemPose.localPosition = furnitureRelationship.ItemParent.InverseTransformPoint(((Component)item).transform.position); savedItemPose.localRotation = Quaternion.Inverse(furnitureRelationship.ItemParent.rotation) * ((Component)item).transform.rotation; return savedItemPose; } private static void Forget(GrabbableObject item) { NetworkManager singleton = NetworkManager.Singleton; if (singleton.IsServer) { hostItems.Remove(((NetworkBehaviour)item).NetworkObjectId); PlaceableObjectsSurfaceRegistry.UnmarkBasedItem(item); } } private static void UnlockablesSynced(StartOfRound round) { if (!((NetworkBehaviour)round).IsServer) { unlockablesSynced = true; RequestAssignments(); } } private static void RequestAssignments() { if (!HatNet.SendToServer(RequestKey, string.Empty)) { Log.Warn("Current item data could not be requested"); } } private static void AssignmentsRequested(ulong clientId, string data) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown int num = savedPositions; int num2 = savedMeshVariants; int num3 = savedMaterialVariants; int num4 = savedBatteries; int num5 = savedChemicals; int num6 = savedSprayPaint; hostItems.Clear(); GrabbableObject[] array = Object.FindObjectsByType<GrabbableObject>((FindObjectsInactive)0, (FindObjectsSortMode)0); foreach (GrabbableObject val in array) { if (ItemExtensions.GetDawnInfo(val.itemProperties) != null) { SaveItem(val, new JObject()); } } savedPositions = num; savedMeshVariants = num2; savedMaterialVariants = num3; savedBatteries = num4; savedChemicals = num5; savedSprayPaint = num6; ItemAssignmentList itemAssignmentList = new ItemAssignmentList { Items = hostItems.Values.Where((ItemAssignment assignment) => !assignment.ValuesOnly).ToList(), Variants = hostItems.Values.Where((ItemAssignment assignment) => assignment.ValuesOnly).ToList() }; int num7 = hostItems.Values.Count((ItemAssignment assignment) => assignment.MeshVariant >= 0); int num8 = hostItems.Values.Count((ItemAssignment assignment) => assignment.MaterialVariant >= 0); int num9 = hostItems.Values.Count((ItemAssignment assignment) => assignment.BatteryCharge >= 0f); int num10 = hostItems.Values.Count((ItemAssignment assignment) => assignment.ChemicalFuel >= 0f); int num11 = hostItems.Values.Count((ItemAssignment assignment) => assignment.SprayPaintTank >= 0f); Log.Debug($"Sent {itemAssignmentList.Items.Count} item positions, {num7} mesh variants, {num8} material variants, {num9} battery charges, {num10} TZP fuel, and {num11} spray paint fuel to client {clientId}"); if (!HatNet.SendToClient(AssignmentKey, clientId, JsonUtility.ToJson((object)itemAssignmentList))) { Log.Warn("Current item data could not be returned to a client"); } } private static void AssignmentsReceived(ulong clientId, string data) { NetworkManager singleton = NetworkManager.Singleton; if (singleton.IsServer || clientId != 0L) { return; } try { ItemAssignmentList itemAssignmentList = JsonUtility.FromJson<ItemAssignmentList>(data); StartOfRound instance = StartOfRound.Instance; if (itemAssignmentList?.Items == null) { return; } foreach (ItemAssignment item in itemAssignmentList.Items) { if (item.Pose.parentKind != ItemParentKind.BeltBag) { NetworkRefs.WhenReady<GrabbableObject>((MonoBehaviour)(object)instance, item.ItemNetworkId, (Action<GrabbableObject>)PlaceableObjectsSurfaceRegistry.MarkBasedItem, 8f, 0); } } if (unlockablesSynced) { PlaceClientItems(instance, itemAssignmentList); } } catch (ArgumentException) { Log.Warn("Saved item data could not be read"); } } private static void PlaceClientItems(StartOfRound round, ItemAssignmentList assignments) { foreach (ItemAssignment assignment in assignments.Variants) { NetworkRefs.WhenReady<GrabbableObject>((MonoBehaviour)(object)round, assignment.ItemNetworkId, (Action<GrabbableObject>)delegate(GrabbableObject item) { RestoreItemValues(item, assignment, assignments); }, 8f, 4); } foreach (ItemAssignment assignment2 in assignments.Items) { NetworkRefs.WhenReady<GrabbableObject>((MonoBehaviour)(object)round, assignment2.ItemNetworkId, (Action<GrabbableObject>)delegate(GrabbableObject item) { RestoreItemValues(item, assignment2, assignments); }, 8f, 4); if (assignment2.Pose.parentKind == ItemParentKind.BeltBag) { NetworkRefs.WhenReady<BeltBagItem>((MonoBehaviour)(object)round, assignment2.ParentNetworkId, (Action<BeltBagItem>)delegate(BeltBagItem beltBag) { NetworkRefs.WhenReady<GrabbableObject>((MonoBehaviour)(object)round, assignment2.ItemNetworkId, (Action<GrabbableObject>)delegate(GrabbableObject item) { if (!beltBag.objectsInBag.Contains(item)) { beltBag.PutObjectInBagLocalClient(item); } assignments.PlacedCount++; }, 8f, 4); }, 8f, 0); continue; } if (assignment2.Pose.parentKind == ItemParentKind.Ship) { NetworkRefs.WhenReady<GrabbableObject>((MonoBehaviour)(object)round, assignment2.ItemNetworkId, (Action<GrabbableObject>)delegate(GrabbableObject item) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) assignments.PlacedCount += (GrabbablePlacement.Place((item.isHeld || (Object)(object)item.playerHeldBy != (Object)null || item.isHeldByEnemy) ? null : item, round.elevatorTransform, assignment2.Pose.worldPosition, assignment2.Pose.worldRotation) ? 1 : 0); }, 8f, 4); continue; } NetworkRefs.WhenReady((MonoBehaviour)(object)round, assignment2.ParentNetworkId, (Action<NetworkObject>)delegate(NetworkObject root) { Transform parent = Utility.FurnitureParentFromPath(((Component)root).transform, assignment2.Pose.furnitureParentPath); if (!((Object)(object)parent == (Object)null)) { NetworkRefs.WhenReady<GrabbableObject>((MonoBehaviour)(object)round, assignment2.ItemNetworkId, (Action<GrabbableObject>)delegate(GrabbableObject item) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) assignments.PlacedCount += (Utility.PlaceFurniturePose((item.isHeld || (Object)(object)item.playerHeldBy != (Object)null || item.isHeldByEnemy) ? null : item, parent, assignment2.Pose.localPosition, assignment2.Pose.localRotation) ? 1 : 0); }, 8f, 4); } }, 8f, 0); } ((MonoBehaviour)round).StartCoroutine(ClientPlacementSummary(assignments)); } private static IEnumerator ClientPlacementSummary(ItemAssignmentList assignments) { yield return (object)new WaitForSecondsRealtime(16.25f); Log.Info($"Client restored {assignments.PlacedCount} of {assignments.Items.Count} item positions, {assignments.MeshVariantCount} mesh variants, {assignments.MaterialVariantCount} material variants, {assignments.BatteryCount} battery charges, {assignments.ChemicalCount} TZP fuel and {assignments.SprayPaintCount} spray paint fuel from server"); } private static void RestoreItemValues(GrabbableObject item, ItemAssignment assignment, ItemAssignmentList assignments) { //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Expected O, but got Unknown Mesh[] meshVariants = item.itemProperties.meshVariants; Material[] materialVariants = item.itemProperties.materialVariants; string text = (NetworkManager.Singleton.IsServer ? "Host" : "Client"); if (assignment.MeshVariant >= 0) { MeshFilter val = default(MeshFilter); if (meshVariants != null && assignment.MeshVariant < meshVariants.Length && ((Component)item).TryGetComponent<MeshFilter>(ref val)) { val.sharedMesh = meshVariants[assignment.MeshVariant]; assignments.MeshVariantCount++; } else { Log.Warn($"{item.itemProperties.itemName} mesh variant {assignment.MeshVariant} could not be restored"); } } if (assignment.MaterialVariant >= 0) { MeshRenderer val2 = default(MeshRenderer); if (materialVariants != null && assignment.MaterialVariant < materialVariants.Length && ((Component)item).TryGetComponent<MeshRenderer>(ref val2)) { ((Renderer)val2).sharedMaterial = materialVariants[assignment.MaterialVariant]; assignments.MaterialVariantCount++; } else { Log.Warn($"{item.itemProperties.itemName} material variant {assignment.MaterialVariant} could not be restored"); } } if (assignment.BatteryCharge >= 0f) { item.insertedBattery = new Battery(assignment.BatteryCharge <= 0f, assignment.BatteryCharge); item.ChargeBatteries(); assignments.BatteryCount++; Log.Debug($"{text} restored battery for {item.itemProperties.itemName} to {assignment.BatteryCharge * 100f:F1}% ({assignment.BatteryCharge:F4})"); } if (assignment.ChemicalFuel >= 0f) { TetraChemicalItem val3 = (TetraChemicalItem)(object)((item is TetraChemicalItem) ? item : null); if (val3 != null) { val3.fuel = assignment.ChemicalFuel; ((GrabbableObject)val3).itemUsedUp = assignment.ChemicalFuel <= 0f; assignments.ChemicalCount++; Log.Debug($"{text} restored TZP fuel for {item.itemProperties.itemName} to {assignment.ChemicalFuel * 100f:F1}% ({assignment.ChemicalFuel:F4})"); } else { Log.Warn(item.itemProperties.itemName + " TZP fuel could not be restored"); } } if (assignment.SprayPaintTank >= 0f) { SprayPaintItem val4 = (SprayPaintItem)(object)((item is SprayPaintItem) ? item : null); if (val4 != null) { val4.sprayCanTank = assignment.SprayPaintTank; assignments.SprayPaintCount++; Log.Debug($"{text} restored spray paint fuel for {item.itemProperties.itemName} to {assignment.SprayPaintTank * 100f:F1}% ({assignment.SprayPaintTank:F4})"); } else { Log.Warn(item.itemProperties.itemName + " spray paint fuel could not be restored"); } } } private static void RoundCleared(StartOfRound round) { pending.Clear(); pendingItemValues.Clear(); loadedItems.Clear(); hostItems.Clear(); PlaceableObjectsSurfaceRegistry.ClearRoundItems(); } private static void LogSavedItems() { Log.Info($"Saved {savedPositions} item positions, {savedMeshVariants} mesh variants, {savedMaterialVariants} material variants, {savedBatteries} battery charges, {savedChemicals} TZP fuel, and {savedSprayPaint} spray paint fuel"); savedPositions = 0; savedMeshVariants = 0; savedMaterialVariants = 0; savedBatteries = 0; savedChemicals = 0; savedSprayPaint = 0; } } internal enum ItemParentKind { Ship, Furniture, BeltBag } [Serializable] internal class SavedItemPose { public ItemParentKind parentKind; public Vector3 worldPosition; public Vector3 localPosition; public Quaternion worldRotation; public Quaternion localRotation; public string furnitureKey = string.Empty; public string furnitureParentPath = string.Empty; public string parentItemId = string.Empty; } [Serializable] internal class ItemAssignment { public ulong ItemNetworkId; public ulong ParentNetworkId; public int MeshVariant = -1; public int MaterialVariant = -1; public float BatteryCharge = -1f; public float ChemicalFuel = -1f; public float SprayPaintTank = -1f; public SavedItemPose Pose; internal bool ValuesOnly; internal ItemAssignment(GrabbableObject item, SavedItemPose pose, FurnitureRelationship? furniture = null) { ItemNetworkId = ((NetworkBehaviour)item).NetworkObjectId; ParentNetworkId = ((furniture != null && (Object)(object)furniture.ShipObject.parentObject != (Object)null) ? ((NetworkBehaviour)furniture.ShipObject.parentObject).NetworkObjectId : 0); Pose = pose; } internal ItemAssignment(GrabbableObject item, int meshVariant, int materialVariant, float batteryCharge, float chemicalFuel, float sprayPaintTank) { ItemNetworkId = ((NetworkBehaviour)item).NetworkObjectId; MeshVariant = meshVariant; MaterialVariant = materialVariant; BatteryCharge = batteryCharge; ChemicalFuel = chemicalFuel; SprayPaintTank = sprayPaintTank; ValuesOnly = true; } } [Serializable] internal class ItemAssignmentList { public List<ItemAssignment> Items = new List<ItemAssignment>(); public List<ItemAssignment> Variants = new List<ItemAssignment>(); internal int PlacedCount; internal int MeshVariantCount; internal int MaterialVariantCount; internal int BatteryCount; internal int ChemicalCount; internal int SprayPaintCount; } } namespace BasedItemSaving.API { public readonly struct PlaceableObjectsSurfaceRegistrationAPI { public string OwnerGuid { get; } public Type SurfaceType { get; } public bool AllowAutomaticSaving { get; } public PlaceableObjectsSurfaceRegistrationAPI(string ownerGuid, Type surfaceType, bool allowAutomaticSaving) { OwnerGuid = ownerGuid; SurfaceType = surfaceType; AllowAutomaticSaving = allowAutomaticSaving; } } public static class PlaceableObjectsSurfaceRegistry { private static readonly List<PlaceableObjectsSurfaceRegistrationAPI> registrations = new List<PlaceableObjectsSurfaceRegistrationAPI>(); private static readonly HashSet<GrabbableObject> ignoredItems = new HashSet<GrabbableObject>(); private static readonly HashSet<GrabbableObject> basedItems = new HashSet<GrabbableObject>(); public static void Register<TSurface>(string ownerGuid, bool allowAutomaticSaving) where TSurface : PlaceableObjectsSurface { Type surfaceType = typeof(TSurface); if (!registrations.Any((PlaceableObjectsSurfaceRegistrationAPI entry) => entry.OwnerGuid == ownerGuid && entry.SurfaceType == surfaceType)) { registrations.Add(new PlaceableObjectsSurfaceRegistrationAPI(ownerGuid, surfaceType, allowAutomaticSaving)); } } public static bool TryGet(PlaceableObjectsSurface surface, out PlaceableObjectsSurfaceRegistrationAPI registration) { registration = registrations.FirstOrDefault((PlaceableObjectsSurfaceRegistrationAPI entry) => entry.SurfaceType.IsInstanceOfType(surface)); return registration.SurfaceType != null; } public static void Ignore(GrabbableObject item) { ignoredItems.Add(item); } public static void Allow(GrabbableObject item) { ignoredItems.Remove(item); } public static bool IsIgnored(GrabbableObject item) { if ((Object)(object)item != (Object)null) { if (!ignoredItems.Contains(item)) { return basedItems.Contains(item); } return true; } return false; } internal static bool IsExplicitlyIgnored(GrabbableObject item) { if ((Object)(object)item != (Object)null) { return ignoredItems.Contains(item); } return false; } internal static void MarkBasedItem(GrabbableObject item) { basedItems.Add(item); } internal static void UnmarkBasedItem(GrabbableObject item) { basedItems.Remove(item); } internal static void ClearRoundItems() { ignoredItems.Clear(); basedItems.Clear(); } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }