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 LostAndFound v0.1.2
LostAndFound/lostandfound.dll
Decompiled 5 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using Unity.Collections; using Unity.Netcode; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("LostAndFound")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.2.0")] [assembly: AssemblyInformationalVersion("0.1.2")] [assembly: AssemblyProduct("LostAndFound")] [assembly: AssemblyTitle("LostAndFound")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.2.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 LostAndFound { internal sealed class BinModel : IDisposable { internal readonly GameObject Root; private readonly List<Material> materials = new List<Material>(); private readonly InteractTrigger trigger; internal Vector3 OutletPosition => Root.transform.TransformPoint(new Vector3(0f, -0.15f, -0.57f)); internal BinModel(Terminal terminal, Transform furniture) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_0050: 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_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: 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_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) Root = new GameObject("Lost and Found trash can"); Root.transform.SetParent(furniture, false); trigger = ((Component)furniture).GetComponentInChildren<InteractTrigger>(true); try { Material material = Mat(new Color(0.25f, 0.3f, 0.32f)); Material material2 = Mat(new Color(0.035f, 0.045f, 0.05f)); Material material3 = Mat(new Color(0.95f, 0.65f, 0.12f)); Part("Can", (PrimitiveType)2, new Vector3(0f, -0.025f, 0f), new Vector3(0.5f, 0.33f, 0.5f), material); Part("Bottom rim", (PrimitiveType)2, new Vector3(0f, -0.35f, 0f), new Vector3(0.54f, 0.025f, 0.54f), material2); Part("Lid", (PrimitiveType)2, new Vector3(0f, 0.315f, 0f), new Vector3(0.56f, 0.035f, 0.56f), material3); Part("Lid handle", (PrimitiveType)3, new Vector3(0f, 0.375f, 0f), new Vector3(0.16f, 0.04f, 0.05f), material2); for (int i = 0; i < 12; i++) { float num = (float)i * MathF.PI / 6f; Part("Can rib", (PrimitiveType)3, new Vector3(Mathf.Sin(num) * 0.246f, -0.035f, Mathf.Cos(num) * 0.246f), new Vector3(0.018f, 0.55f, 0.018f), material2); } Part("Pedal", (PrimitiveType)3, new Vector3(0f, -0.34f, -0.29f), new Vector3(0.18f, 0.025f, 0.16f), material3); trigger.hoverTip = "Empty Lost and Found : [E]"; trigger.disabledHoverTip = "Lost and Found unavailable"; trigger.oneHandedItemAllowed = (trigger.twoHandedItemAllowed = true); trigger.holdInteraction = false; trigger.interactCooldown = true; trigger.cooldownTime = 0.5f; InteractTrigger[] componentsInChildren = ((Component)terminal).GetComponentsInChildren<InteractTrigger>(true); foreach (InteractTrigger val in componentsInChildren) { if ((Object)(object)val != (Object)(object)trigger && Object.op_Implicit((Object)(object)val.hoverIcon)) { trigger.hoverIcon = val.hoverIcon; trigger.disabledHoverIcon = val.hoverIcon; break; } } } catch { Dispose(); throw; } } private Material Mat(Color color) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) Shader obj = Shader.Find("HDRP/Lit") ?? Shader.Find("HDRP/Unlit"); if (!Object.op_Implicit((Object)(object)obj)) { throw new InvalidOperationException("Cannot find ship model shader."); } Material val = new Material(obj); val.SetColor("_BaseColor", color); if (val.HasProperty("_UnlitColor")) { val.SetColor("_UnlitColor", color); } if (val.HasProperty("_Metallic")) { val.SetFloat("_Metallic", 0.6f); } materials.Add(val); return val; } private void Part(string name, PrimitiveType type, Vector3 position, Vector3 scale, Material material) { //IL_0000: 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_0036: Unknown result type (might be due to invalid IL or missing references) GameObject obj = GameObject.CreatePrimitive(type); ((Object)obj).name = name; obj.transform.SetParent(Root.transform, false); obj.transform.localPosition = position; obj.transform.localScale = scale; Collider component = obj.GetComponent<Collider>(); component.enabled = false; Object.Destroy((Object)(object)component); ((Renderer)obj.GetComponent<MeshRenderer>()).sharedMaterial = material; } internal void SetReady(bool ready) { trigger.interactable = ready; } public void Dispose() { if (Object.op_Implicit((Object)(object)trigger)) { trigger.interactable = false; } if (Object.op_Implicit((Object)(object)Root)) { Object.Destroy((Object)(object)Root); } foreach (Material material in materials) { if (Object.op_Implicit((Object)(object)material)) { Object.Destroy((Object)(object)material); } } materials.Clear(); } } internal sealed class DispenserSnapshot { internal const int Protocol = 3; internal const int Size = 38; internal bool Enabled; internal bool Available; internal ulong NetworkId; internal float X; internal float Y; internal float Z; internal float Pitch; internal float Yaw; internal float Roll; internal static bool HasPayload(int bufferLength, int position) { if (position >= 0 && bufferLength >= position) { return bufferLength - position == 38; } return false; } internal byte[] Encode() { using MemoryStream memoryStream = new MemoryStream(38); using BinaryWriter binaryWriter = new BinaryWriter(memoryStream); binaryWriter.Write(3); binaryWriter.Write(Enabled); binaryWriter.Write(Available); binaryWriter.Write(NetworkId); binaryWriter.Write(X); binaryWriter.Write(Y); binaryWriter.Write(Z); binaryWriter.Write(Pitch); binaryWriter.Write(Yaw); binaryWriter.Write(Roll); return memoryStream.ToArray(); } internal static bool TryDecode(byte[] data, out DispenserSnapshot snapshot) { snapshot = null; if (data == null || data.Length != 38 || data[4] > 1 || data[5] > 1) { return false; } using MemoryStream input = new MemoryStream(data, writable: false); using BinaryReader binaryReader = new BinaryReader(input); if (binaryReader.ReadInt32() != 3) { return false; } DispenserSnapshot dispenserSnapshot = new DispenserSnapshot { Enabled = binaryReader.ReadBoolean(), Available = binaryReader.ReadBoolean(), NetworkId = binaryReader.ReadUInt64(), X = binaryReader.ReadSingle(), Y = binaryReader.ReadSingle(), Z = binaryReader.ReadSingle(), Pitch = binaryReader.ReadSingle(), Yaw = binaryReader.ReadSingle(), Roll = binaryReader.ReadSingle() }; float[] array = new float[6] { dispenserSnapshot.X, dispenserSnapshot.Y, dispenserSnapshot.Z, dispenserSnapshot.Pitch, dispenserSnapshot.Yaw, dispenserSnapshot.Roll }; foreach (float f in array) { if (float.IsNaN(f) || float.IsInfinity(f)) { return false; } } snapshot = dispenserSnapshot; return true; } } internal static class Furniture { internal const string UnlockableName = "Lost and Found"; internal static uint PrefabHash; internal static UnlockableItem Item; internal static GameObject Prefab; internal static void Register() { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_025e: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Unknown result type (might be due to invalid IL or missing references) //IL_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_02cd: Unknown result type (might be due to invalid IL or missing references) //IL_02d7: Expected O, but got Unknown //IL_02d8: Unknown result type (might be due to invalid IL or missing references) //IL_02e2: Expected O, but got Unknown //IL_02e3: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Expected O, but got Unknown //IL_02ee: Unknown result type (might be due to invalid IL or missing references) //IL_02f8: Expected O, but got Unknown //IL_02f9: Unknown result type (might be due to invalid IL or missing references) //IL_0303: Expected O, but got Unknown //IL_0304: Unknown result type (might be due to invalid IL or missing references) //IL_030e: Expected O, but got Unknown //IL_0314: Unknown result type (might be due to invalid IL or missing references) //IL_0319: Unknown result type (might be due to invalid IL or missing references) //IL_0324: Unknown result type (might be due to invalid IL or missing references) //IL_032f: Unknown result type (might be due to invalid IL or missing references) //IL_0336: Unknown result type (might be due to invalid IL or missing references) //IL_033d: Unknown result type (might be due to invalid IL or missing references) //IL_0344: Unknown result type (might be due to invalid IL or missing references) //IL_034b: Unknown result type (might be due to invalid IL or missing references) //IL_0352: Unknown result type (might be due to invalid IL or missing references) //IL_035e: Expected O, but got Unknown Assembly assembly = ((object)Chainloader.PluginInfos["evaisa.lethallib"].Instance).GetType().Assembly; Prefab = (GameObject)assembly.GetType("LethalLib.Modules.NetworkPrefabs").GetMethod("CreateNetworkPrefab", new Type[1] { typeof(string) }).Invoke(null, new object[1] { "achachala.lostandfound.Furniture.v2" }); PrefabHash = StablePrefabHash("achachala.lostandfound.Furniture.v2"); typeof(NetworkObject).GetField("GlobalObjectIdHash", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).SetValue(Prefab.GetComponent<NetworkObject>(), PrefabHash); int num = LayerMask.NameToLayer("PlaceableShipObjects"); if (num < 0) { throw new InvalidOperationException("Missing native PlaceableShipObjects layer."); } Prefab.layer = num; Prefab.tag = "PlaceableObject"; AudioSource obj = Prefab.AddComponent<AudioSource>(); obj.playOnAwake = false; obj.spatialBlend = 1f; obj.rolloffMode = (AudioRolloffMode)1; obj.minDistance = 1f; obj.maxDistance = 12f; AutoParentToShip parentObject = Prefab.AddComponent<AutoParentToShip>(); PlaceableShipObject val = Prefab.AddComponent<PlaceableShipObject>(); BoxCollider val2 = Prefab.AddComponent<BoxCollider>(); val2.size = new Vector3(0.58f, 0.78f, 0.58f); GameObject val3 = GameObject.CreatePrimitive((PrimitiveType)3); Mesh val4 = Object.Instantiate<Mesh>(val3.GetComponent<MeshFilter>().sharedMesh); Vector3[] vertices = val4.vertices; for (int i = 0; i < vertices.Length; i++) { vertices[i] = Vector3.Scale(vertices[i], new Vector3(0.55f, 0.77f, 0.55f)); } val4.vertices = vertices; val4.RecalculateBounds(); MeshFilter val5 = Prefab.AddComponent<MeshFilter>(); val5.sharedMesh = val4; MeshRenderer obj2 = Prefab.AddComponent<MeshRenderer>(); ((Renderer)obj2).sharedMaterial = ((Renderer)val3.GetComponent<MeshRenderer>()).sharedMaterial; Object.Destroy((Object)(object)val3); ((Renderer)obj2).enabled = false; val.parentObject = parentObject; val.mainMesh = val5; val.mainTransform = Prefab.transform; val.placeObjectCollider = (Collider)(object)val2; val.yOffset = 0.39f; val.AllowPlacementOnCounters = true; val.AllowPlacementOnWalls = false; val.overrideWallOffset = true; val.wallOffset = 0.18f; Prefab.AddComponent<FurnitureMarker>(); GameObject val6 = new GameObject("Empty bin interaction"); val6.transform.SetParent(Prefab.transform, false); int num2 = LayerMask.NameToLayer("InteractableObject"); val6.layer = ((num2 >= 0) ? num2 : 9); val6.tag = "InteractTrigger"; val6.AddComponent<BinMarker>(); val6.AddComponent<BoxCollider>().size = new Vector3(0.56f, 0.76f, 0.56f); InteractTrigger obj3 = val6.AddComponent<InteractTrigger>(); obj3.onInteract = new InteractEvent(); obj3.onInteractEarly = new InteractEvent(); obj3.onInteractEarlyOtherClients = new InteractEvent(); obj3.onStopInteract = new InteractEvent(); obj3.onCancelAnimation = new InteractEvent(); obj3.holdingInteractEvent = new InteractEventFloat(); obj3.interactable = false; Item = new UnlockableItem { unlockableName = "Lost and Found", prefabObject = Prefab, unlockableType = 1, IsPlaceable = true, canBeStored = true, maxNumber = 1, alreadyUnlocked = true, spawnPrefab = true }; MethodInfo methodInfo = assembly.GetType("LethalLib.Modules.Unlockables").GetMethods(BindingFlags.Static | BindingFlags.Public).Single((MethodInfo m) => m.Name == "RegisterUnlockable" && m.GetParameters().Length == 3 && m.GetParameters()[0].ParameterType == typeof(UnlockableItem) && m.GetParameters()[1].ParameterType == typeof(int)); Type parameterType = methodInfo.GetParameters()[2].ParameterType; methodInfo.Invoke(null, new object[3] { Item, -1, Enum.ToObject(parameterType, 0) }); } internal static void Configure() { //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) StartOfRound instance = StartOfRound.Instance; if (!Object.op_Implicit((Object)(object)instance) || !Object.op_Implicit((Object)(object)Prefab)) { return; } int num = ResolveItem(); if (num < 0) { return; } PlaceableShipObject component = Prefab.GetComponent<PlaceableShipObject>(); AutoParentToShip component2 = Prefab.GetComponent<AutoParentToShip>(); component.unlockableID = (component2.unlockableID = num); PlaceableShipObject[] array = Object.FindObjectsOfType<PlaceableShipObject>(); foreach (PlaceableShipObject val in array) { if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.placeObjectSFX) && !Object.op_Implicit((Object)(object)((Component)val).GetComponent<FurnitureMarker>())) { component.placeObjectSFX = val.placeObjectSFX; break; } } component2.overrideOffset = true; component2.positionOffset = instance.elevatorTransform.InverseTransformPoint(DefaultPosition()); component2.rotationOffset = instance.elevatorTransform.eulerAngles; } internal static Vector3 DefaultPosition() { //IL_000a: 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) //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_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0089: 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_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0066: 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) Bounds bounds = StartOfRound.Instance.shipInnerRoomBounds.bounds; Vector3 center = ((Bounds)(ref bounds)).center; RaycastHit val = default(RaycastHit); if (Physics.Raycast(center, Vector3.down, ref val, ((Bounds)(ref bounds)).size.y + 5f, LayerMask.GetMask(new string[2] { "Room", "Colliders" }), (QueryTriggerInteraction)1)) { return ((RaycastHit)(ref val)).point + Vector3.up * 0.4f; } return new Vector3(center.x, ((Bounds)(ref bounds)).min.y + 0.4f, center.z); } internal static uint StablePrefabHash(string name) { uint num = 2166136261u; foreach (char c in name) { num = (num ^ c) * 16777619; } return num; } internal static int ResolveItem() { StartOfRound instance = StartOfRound.Instance; if (!Object.op_Implicit((Object)(object)instance) || !Object.op_Implicit((Object)(object)instance.unlockablesList)) { return -1; } int num = instance.unlockablesList.unlockables.FindIndex((UnlockableItem item) => item != null && string.Equals(item.unlockableName, "Lost and Found", StringComparison.Ordinal)); if (num >= 0) { Item = instance.unlockablesList.unlockables[num]; } return num; } internal static bool BindReplica(FurnitureMarker marker, bool register = true) { int num = ResolveItem(); if (num < 0) { return false; } PlaceableShipObject component = ((Component)marker).GetComponent<PlaceableShipObject>(); AutoParentToShip component2 = ((Component)marker).GetComponent<AutoParentToShip>(); component.unlockableID = (component2.unlockableID = num); component.parentObject = component2; StartOfRound instance = StartOfRound.Instance; if (register && instance.SpawnedShipUnlockables != null) { instance.SpawnedShipUnlockables[num] = ((Component)marker).gameObject; } return true; } internal static FurnitureMarker? Find(ulong? networkId = null) { NetworkManager singleton = NetworkManager.Singleton; if (!Object.op_Implicit((Object)(object)singleton) || singleton.SpawnManager == null || ResolveItem() < 0) { return null; } if (networkId.HasValue) { if (!singleton.SpawnManager.SpawnedObjects.TryGetValue(networkId.Value, out var value)) { return null; } FurnitureMarker component = ((Component)value).GetComponent<FurnitureMarker>(); if (!Object.op_Implicit((Object)(object)component) || !BindReplica(component)) { return null; } return component; } foreach (NetworkObject value2 in singleton.SpawnManager.SpawnedObjects.Values) { if (Object.op_Implicit((Object)(object)value2) && value2.IsSpawned) { FurnitureMarker component2 = ((Component)value2).GetComponent<FurnitureMarker>(); if (Object.op_Implicit((Object)(object)component2) && BindReplica(component2)) { return component2; } } } return null; } } public sealed class FurnitureMarker : MonoBehaviour { } [HarmonyPatch(typeof(AutoParentToShip), "Awake")] internal static class FurnitureAwakePatch { [HarmonyPrefix] private static void Prefix(AutoParentToShip __instance) { FurnitureMarker component = ((Component)__instance).GetComponent<FurnitureMarker>(); if (Object.op_Implicit((Object)(object)component)) { Furniture.BindReplica(component, register: false); } } } internal static class InventoryTraversal { internal static HashSet<ulong> Expand<T>(IEnumerable<T> roots, Func<T, ulong?> identity, Func<T, IEnumerable<T>> children) { HashSet<ulong> hashSet = new HashSet<ulong>(); Stack<T> stack = new Stack<T>(roots); while (stack.Count > 0) { T arg = stack.Pop(); ulong? num = identity(arg); if (!num.HasValue || !hashSet.Add(num.Value)) { continue; } foreach (T item in children(arg)) { stack.Push(item); } } return hashSet; } internal static int Depth(ulong id, Func<ulong, ulong?> parent) { HashSet<ulong> hashSet = new HashSet<ulong>(); int num = 0; while (hashSet.Add(id)) { ulong? num2 = parent(id); if (!num2.HasValue) { break; } num++; id = num2.Value; } return num; } internal static bool AncestorsAllowed(ulong id, Func<ulong, ulong?> parent, Func<ulong, bool> allowed) { HashSet<ulong> hashSet = new HashSet<ulong> { id }; do { ulong? num = parent(id); if (!num.HasValue) { return true; } id = num.Value; } while (hashSet.Add(id) && allowed(id)); return false; } } [BepInPlugin("achachala.lostandfound", "Lost and Found", "0.1.2")] [BepInDependency("evaisa.lethallib", "1.2.0")] public sealed class Plugin : BaseUnityPlugin { public const string Id = "achachala.lostandfound"; public const string Version = "0.1.2"; private const string Hello = "achachala.lostandfound.hello.v3"; private const string Settings = "achachala.lostandfound.settings.v3"; private const string Dispense = "achachala.lostandfound.dispense.v1"; internal static Plugin Instance; private ConfigEntry<bool> enabledConfig; private ConfigEntry<float> cooldownConfig; private readonly RequestGate requests = new RequestGate(); private readonly HashSet<ulong> handshakePeers = new HashSet<ulong>(); private NetworkManager? network; private Terminal? terminal; private BinModel? model; private FurnitureMarker? furniture; private FurnitureMarker? positionedFurniture; private Harmony harmony; private bool attached; private bool haveSettings; private bool activeEnabled; private float nextUpdate; private float nextHello; private bool? collidersEnabled; private bool hostHasFurniture; private ulong hostFurnitureId; private Vector3 hostPositionOffset; private Vector3 hostRotationOffset; private bool applyHostPlacement; private float nextDiagnostic; private string lastWaitReason = ""; private uint localSequence; private float nextModelAttempt; internal RecoveryService Recovery; internal bool RecoveryEnabled => enabledConfig.Value; private void Awake() { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown Instance = this; enabledConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Recovery", "Enabled", true, "Host: enable the Lost and Found bin."); cooldownConfig = ((BaseUnityPlugin)this).Config.Bind<float>("Recovery", "SecondsBetweenUses", 0.35f, new ConfigDescription("Host: minimum time between empty-bin requests.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>())); Recovery = new RecoveryService(this); Furniture.Register(); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Registered Lost and Found network prefab hash={Furniture.PrefabHash}."); harmony = new Harmony("achachala.lostandfound"); harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Lost and Found 0.1.2 loaded. Install on host AND all clients, with LethalLib."); } private unsafe void Update() { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Expected O, but got Unknown //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Expected O, but got Unknown //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_02d5: Unknown result type (might be due to invalid IL or missing references) //IL_02da: Unknown result type (might be due to invalid IL or missing references) //IL_02e1: Unknown result type (might be due to invalid IL or missing references) //IL_02e6: Unknown result type (might be due to invalid IL or missing references) //IL_05fe: Unknown result type (might be due to invalid IL or missing references) //IL_0603: Unknown result type (might be due to invalid IL or missing references) //IL_060f: Unknown result type (might be due to invalid IL or missing references) //IL_0614: Unknown result type (might be due to invalid IL or missing references) //IL_0628: Unknown result type (might be due to invalid IL or missing references) //IL_062a: Unknown result type (might be due to invalid IL or missing references) //IL_0639: Unknown result type (might be due to invalid IL or missing references) //IL_063b: Unknown result type (might be due to invalid IL or missing references) //IL_063f: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton != (Object)(object)network || (attached && (!Object.op_Implicit((Object)(object)singleton) || !singleton.IsListening))) { Detach(); } if (!Object.op_Implicit((Object)(object)singleton) || !singleton.IsListening || singleton.CustomMessagingManager == null) { return; } if (!attached) { network = singleton; singleton.CustomMessagingManager.RegisterNamedMessageHandler("achachala.lostandfound.hello.v3", new HandleNamedMessageDelegate(ReceiveHello)); singleton.CustomMessagingManager.RegisterNamedMessageHandler("achachala.lostandfound.settings.v3", new HandleNamedMessageDelegate(ReceiveSettings)); singleton.CustomMessagingManager.RegisterNamedMessageHandler("achachala.lostandfound.dispense.v1", new HandleNamedMessageDelegate(ReceiveDispense)); Recovery.Attach(singleton); singleton.OnClientDisconnectCallback += Disconnected; singleton.OnClientConnectedCallback += Connected; attached = true; nextHello = (nextUpdate = 0f); } if (singleton.IsServer) { Recovery.Observe(); } Recovery.ApplyPending(); if (Time.unscaledTime < nextUpdate) { return; } nextUpdate = Time.unscaledTime + 0.25f; if (singleton.IsServer) { activeEnabled = enabledConfig.Value; haveSettings = true; Recovery.EnsureLoaded(); } else if (singleton.IsConnectedClient && Time.unscaledTime >= nextHello) { nextHello = Time.unscaledTime + 2f; FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(4, (Allocator)2, -1); try { int num = 3; ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref num, default(ForPrimitives)); singleton.CustomMessagingManager.SendNamedMessage("achachala.lostandfound.hello.v3", 0uL, val, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } if (!Object.op_Implicit((Object)(object)terminal)) { terminal = Object.FindObjectOfType<Terminal>(); } FurnitureMarker furnitureMarker = (singleton.IsServer ? Furniture.Find() : ((haveSettings && hostHasFurniture) ? Furniture.Find(hostFurnitureId) : null)); if ((Object)(object)furnitureMarker != (Object)(object)furniture) { DestroyModel(); furniture = furnitureMarker; collidersEnabled = null; } if (!singleton.IsServer && Object.op_Implicit((Object)(object)furnitureMarker) && applyHostPlacement && Furniture.BindReplica(furnitureMarker)) { PlaceableShipObject component = ((Component)furnitureMarker).GetComponent<PlaceableShipObject>(); ShipBuildModeManager instance = ShipBuildModeManager.Instance; if (!Object.op_Implicit((Object)(object)instance) || !instance.InBuildMode || AccessTools.Field(typeof(ShipBuildModeManager), "placingObject").GetValue(instance) != component) { AutoParentToShip component2 = ((Component)furnitureMarker).GetComponent<AutoParentToShip>(); component2.overrideOffset = true; component2.disableObject = false; component2.positionOffset = hostPositionOffset; component2.rotationOffset = hostRotationOffset; component2.MoveToOffset(); if (!((Component)furnitureMarker).gameObject.activeSelf) { ((Component)furnitureMarker).gameObject.SetActive(true); } Furniture.Item.inStorage = false; applyHostPlacement = false; } } if (!singleton.IsServer && (!haveSettings || !hostHasFurniture || !Object.op_Implicit((Object)(object)furniture))) { string text = ((!haveSettings) ? "waiting for host handshake (confirm every player uses 0.1.2)" : ((!hostHasFurniture) ? "host reports no deployed Lost and Found" : $"waiting for network object {hostFurnitureId}; check matching mods/dependencies and prefab registration")); if (Time.unscaledTime >= nextDiagnostic || text != lastWaitReason) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Lost and Found client: " + text)); nextDiagnostic = Time.unscaledTime + 20f; lastWaitReason = text; } } if (!singleton.IsServer && Object.op_Implicit((Object)(object)furnitureMarker) && hostHasFurniture) { Furniture.Item.inStorage = false; } bool flag = haveSettings && activeEnabled && (singleton.IsServer ? (!Furniture.Item.inStorage) : hostHasFurniture); if (Object.op_Implicit((Object)(object)furniture) && collidersEnabled != flag) { Collider[] componentsInChildren = ((Component)furniture).GetComponentsInChildren<Collider>(true); for (int num = 0; num < componentsInChildren.Length; num++) { componentsInChildren[num].enabled = flag; } collidersEnabled = flag; } if (!flag || !Object.op_Implicit((Object)(object)terminal) || !Object.op_Implicit((Object)(object)furniture)) { DestroyModel(); return; } if (model == null || !Object.op_Implicit((Object)(object)model.Root)) { if (Time.unscaledTime < nextModelAttempt) { return; } try { model = new BinModel(terminal, ((Component)furniture).transform); ((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("Created movable ship Lost and Found bin on {0}, object={1}, unlockable={2}.", singleton.IsServer ? "host" : "client", ((Component)furniture).GetComponent<NetworkObject>().NetworkObjectId, Furniture.ResolveItem())); } catch (Exception ex) { nextModelAttempt = Time.unscaledTime + 10f; ((BaseUnityPlugin)this).Logger.LogError((object)("Could not build Lost and Found; will retry in 10 seconds: " + ex)); return; } } if (singleton.IsServer && (Object)(object)positionedFurniture != (Object)(object)furniture && Object.op_Implicit((Object)(object)ShipBuildModeManager.Instance) && Object.op_Implicit((Object)(object)GameNetworkManager.Instance) && Object.op_Implicit((Object)(object)GameNetworkManager.Instance.localPlayerController)) { string currentSaveFileName = GameNetworkManager.Instance.currentSaveFileName; if (!ES3.Load<bool>("LostAndFound.CenteredPlacementV1", currentSaveFileName, false) || !Furniture.Item.hasBeenMoved) { NetworkObject component3 = ((Component)furniture).GetComponent<NetworkObject>(); Vector3 val2 = Furniture.DefaultPosition(); Vector3 eulerAngles = StartOfRound.Instance.elevatorTransform.eulerAngles; PlaceableShipObject component4 = ((Component)furniture).GetComponent<PlaceableShipObject>(); ShipBuildModeManager.Instance.PlaceShipObject(val2, eulerAngles, component4, false); ShipBuildModeManager.Instance.PlaceShipObjectServerRpc(val2, eulerAngles, new NetworkObjectReference(component3), (int)GameNetworkManager.Instance.localPlayerController.playerClientId); ES3.Save<bool>("LostAndFound.CenteredPlacementV1", true, currentSaveFileName); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Placed Lost and Found bin in the centre of the ship; subsequent furniture moves are preserved."); } positionedFurniture = furniture; } model.SetReady(ready: true); } internal void ConfigureFurniture() { Furniture.Configure(); } internal void LogInfo(string message) { ((BaseUnityPlugin)this).Logger.LogInfo((object)message); } internal void Error(string message, Exception error) { ((BaseUnityPlugin)this).Logger.LogError((object)(message + ": " + error)); } private void Connected(ulong client) { if (Object.op_Implicit((Object)(object)network) && network.IsServer && client != network.LocalClientId) { SendState(client); } } private unsafe void SendState(ulong client) { //IL_0074: 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_0079: 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_0082: 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_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: 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_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)network) || !network.IsServer || !network.ConnectedClients.ContainsKey(client)) { return; } FurnitureMarker furnitureMarker = Furniture.Find(); bool flag = Object.op_Implicit((Object)(object)furnitureMarker) && !Furniture.Item.inStorage; AutoParentToShip val = (flag ? ((Component)furnitureMarker).GetComponent<AutoParentToShip>() : null); Vector3 val2 = (Object.op_Implicit((Object)(object)val) ? val.positionOffset : Vector3.zero); Vector3 val3 = (Object.op_Implicit((Object)(object)val) ? val.rotationOffset : Vector3.zero); byte[] array = new DispenserSnapshot { Enabled = enabledConfig.Value, Available = flag, NetworkId = (flag ? ((Component)furnitureMarker).GetComponent<NetworkObject>().NetworkObjectId : 0), X = val2.x, Y = val2.y, Z = val2.z, Pitch = val3.x, Yaw = val3.y, Roll = val3.z }.Encode(); FastBufferWriter val4 = default(FastBufferWriter); ((FastBufferWriter)(ref val4))..ctor(38, (Allocator)2, -1); try { ((FastBufferWriter)(ref val4)).WriteBytesSafe(array, array.Length, 0); network.CustomMessagingManager.SendNamedMessage("achachala.lostandfound.settings.v3", client, val4, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val4))/*cast due to .constrained prefix*/).Dispose(); } } private void ReceiveHello(ulong sender, FastBufferReader reader) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)network) || !network.IsServer || !network.ConnectedClients.ContainsKey(sender) || !((FastBufferReader)(ref reader)).TryBeginRead(4)) { return; } int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives)); if (num == 3) { if (handshakePeers.Add(sender)) { ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Client {sender} acknowledged Lost and Found protocol {num}."); } SendState(sender); Recovery.SendPending(sender); } } private void ReceiveSettings(ulong sender, FastBufferReader reader) { //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)network) || network.IsServer || sender != 0L || !DispenserSnapshot.HasPayload(((FastBufferReader)(ref reader)).Length, ((FastBufferReader)(ref reader)).Position) || !((FastBufferReader)(ref reader)).TryBeginRead(38)) { return; } int length = ((FastBufferReader)(ref reader)).Length; int position = ((FastBufferReader)(ref reader)).Position; byte[] array = new byte[38]; ((FastBufferReader)(ref reader)).ReadBytesSafe(ref array, array.Length, 0); if (DispenserSnapshot.TryDecode(array, out DispenserSnapshot snapshot)) { bool enabled = snapshot.Enabled; bool available = snapshot.Available; ulong networkId = snapshot.NetworkId; Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(snapshot.X, snapshot.Y, snapshot.Z); Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor(snapshot.Pitch, snapshot.Yaw, snapshot.Roll); if (!haveSettings) { ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Received Lost and Found host handshake: available={available}, object={networkId}, frame={length}, payloadOffset={position}."); } bool flag = !haveSettings || available != hostHasFurniture || networkId != hostFurnitureId || val != hostPositionOffset || val2 != hostRotationOffset; activeEnabled = enabled; hostHasFurniture = available; hostFurnitureId = networkId; hostPositionOffset = val; hostRotationOffset = val2; applyHostPlacement |= flag; haveSettings = true; } } internal unsafe void Interact(PlayerControllerB player) { //IL_0085: 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_00a3: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)network) || !attached || !haveSettings || !activeEnabled || !Object.op_Implicit((Object)(object)player) || !((NetworkBehaviour)player).IsOwner) { return; } uint sequence = ++localSequence; if (network.IsServer) { ExecuteDispense(network.LocalClientId, sequence); } else if (network.IsConnectedClient) { FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(4, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe<uint>(ref sequence, default(ForPrimitives)); network.CustomMessagingManager.SendNamedMessage("achachala.lostandfound.dispense.v1", 0uL, val, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } } private void ReceiveDispense(ulong sender, FastBufferReader reader) { //IL_002b: 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) if (Object.op_Implicit((Object)(object)network) && network.IsServer && ((FastBufferReader)(ref reader)).TryBeginRead(4)) { uint sequence = default(uint); ((FastBufferReader)(ref reader)).ReadValueSafe<uint>(ref sequence, default(ForPrimitives)); ExecuteDispense(sender, sequence); } } private void ExecuteDispense(ulong sender, uint sequence) { //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)network) || !network.IsServer || !enabledConfig.Value || !network.ConnectedClients.ContainsKey(sender) || !Object.op_Implicit((Object)(object)StartOfRound.Instance) || model == null || !Object.op_Implicit((Object)(object)model.Root) || !Object.op_Implicit((Object)(object)furniture) || Furniture.Item.inStorage || ((Component)furniture).GetComponent<PlaceableShipObject>().inUse) { return; } PlayerControllerB val = null; PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val2 in allPlayerScripts) { if (Object.op_Implicit((Object)(object)val2) && val2.actualClientId == sender && val2.isPlayerControlled) { val = val2; break; } } if (Object.op_Implicit((Object)(object)val) && !val.isPlayerDead && val.isInHangarShipRoom) { Vector3 val3 = ((Component)val).transform.position - model.Root.transform.position; if (!(((Vector3)(ref val3)).sqrMagnitude > 9f) && !StartOfRound.Instance.shipIsLeaving && requests.TryAccept(sender, sequence, Time.unscaledTime, cooldownConfig.Value)) { Recovery.Empty(model.OutletPosition); } } } private void Disconnected(ulong client) { requests.Remove(client); handshakePeers.Remove(client); if (Object.op_Implicit((Object)(object)network) && client == network.LocalClientId) { Detach(); } } private void DestroyModel() { model?.Dispose(); model = null; } private void Detach() { //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0124: 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_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)network)) { network.OnClientDisconnectCallback -= Disconnected; network.OnClientConnectedCallback -= Connected; if (network.CustomMessagingManager != null) { network.CustomMessagingManager.UnregisterNamedMessageHandler("achachala.lostandfound.hello.v3"); network.CustomMessagingManager.UnregisterNamedMessageHandler("achachala.lostandfound.settings.v3"); network.CustomMessagingManager.UnregisterNamedMessageHandler("achachala.lostandfound.dispense.v1"); } } Recovery?.Detach(); DestroyModel(); furniture = (positionedFurniture = null); collidersEnabled = null; network = null; terminal = null; attached = (haveSettings = (activeEnabled = false)); requests.Clear(); handshakePeers.Clear(); localSequence = 0u; ((MonoBehaviour)this).StopAllCoroutines(); hostHasFurniture = (applyHostPlacement = false); hostFurnitureId = 0uL; hostPositionOffset = (hostRotationOffset = Vector3.zero); nextDiagnostic = 0f; lastWaitReason = ""; } private void OnDestroy() { Detach(); Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } } public sealed class BinMarker : MonoBehaviour { } [HarmonyPatch(typeof(InteractTrigger), "Interact")] internal static class InteractionPatch { [HarmonyPrefix] [HarmonyPriority(800)] private static bool Prefix(InteractTrigger __instance, Transform __0) { if (!Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<BinMarker>())) { return true; } if (__instance.interactable && Object.op_Implicit((Object)(object)__0) && Object.op_Implicit((Object)(object)Plugin.Instance)) { Plugin.Instance.Interact(((Component)__0).GetComponent<PlayerControllerB>()); } return false; } } [HarmonyPatch(typeof(Terminal), "Awake")] internal static class TerminalFurniturePatch { [HarmonyPostfix] private static void Postfix(Terminal __instance) { Plugin.Instance.ConfigureFurniture(); } } internal sealed class DeathClaims { private readonly Dictionary<ulong, ulong> claims = new Dictionary<ulong, ulong>(); internal void MarkDeath(ulong player, IEnumerable<ulong> items) { foreach (ulong item in items) { claims[item] = player; } } internal void Recovered(ulong item) { claims.Remove(item); } internal bool CanCollect(ulong item, bool ownerDead, bool held, bool inShip, bool eligible) { return claims.ContainsKey(item) && ownerDead && !held && eligible; } internal KeyValuePair<ulong, ulong>[] Snapshot() { KeyValuePair<ulong, ulong>[] array = new KeyValuePair<ulong, ulong>[claims.Count]; int num = 0; foreach (KeyValuePair<ulong, ulong> claim in claims) { array[num++] = claim; } return array; } internal void Clear() { claims.Clear(); } } [Serializable] public sealed class RecoveredItem { public string Name = ""; public string Prefab = ""; public int ItemId; public int Data; public bool HasBattery; public float Charge; public int ScrapValue; public bool UsedUp; } internal sealed class RecoveryService { private const string SaveKey = "LostAndFound.Items.v1"; private const string RestoreMessage = "achachala.lostandfound.restored.v1"; private const string DeathMessage = "achachala.lostandfound.death.v1"; private const string BagRemoveMessage = "achachala.lostandfound.bag-remove.v1"; private readonly Plugin plugin; private NetworkManager? network; private string? saveFile; private bool loadFailed; private readonly List<RecoveredItem> contents = new List<RecoveredItem>(); private readonly DeathClaims claims = new DeathClaims(); private readonly Dictionary<ulong, HashSet<ulong>> inventory = new Dictionary<ulong, HashSet<ulong>>(); private readonly Dictionary<ulong, ulong> lastHolder = new Dictionary<ulong, ulong>(); private readonly Dictionary<ulong, HashSet<ulong>> deathReports = new Dictionary<ulong, HashSet<ulong>>(); private readonly HashSet<ulong> capturedDeaths = new HashSet<ulong>(); private readonly Dictionary<ulong, RestoredItem> restored = new Dictionary<ulong, RestoredItem>(); private readonly Dictionary<ulong, float> pending = new Dictionary<ulong, float>(); private readonly Dictionary<ulong, string> applied = new Dictionary<ulong, string>(); private bool Host { get { if (Object.op_Implicit((Object)(object)network) && network.IsServer) { return network.IsListening; } return false; } } internal RecoveryService(Plugin owner) { plugin = owner; } internal void Attach(NetworkManager manager) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Expected O, but got Unknown network = manager; manager.CustomMessagingManager.RegisterNamedMessageHandler("achachala.lostandfound.restored.v1", new HandleNamedMessageDelegate(ReceiveRestored)); manager.CustomMessagingManager.RegisterNamedMessageHandler("achachala.lostandfound.death.v1", new HandleNamedMessageDelegate(ReceiveDeath)); manager.CustomMessagingManager.RegisterNamedMessageHandler("achachala.lostandfound.bag-remove.v1", new HandleNamedMessageDelegate(ReceiveBagRemove)); } internal void EnsureLoaded() { if (!Host || !Object.op_Implicit((Object)(object)GameNetworkManager.Instance)) { return; } string currentSaveFileName = GameNetworkManager.Instance.currentSaveFileName; if (string.IsNullOrEmpty(currentSaveFileName) || currentSaveFileName == saveFile) { return; } contents.Clear(); EndRound(); saveFile = currentSaveFileName; loadFailed = false; try { string[] array = ES3.Load<string[]>("LostAndFound.Items.v1", currentSaveFileName, Array.Empty<string>()); for (int i = 0; i < array.Length; i++) { RecoveredItem recoveredItem = JsonUtility.FromJson<RecoveredItem>(array[i]); if (recoveredItem == null || string.IsNullOrEmpty(recoveredItem.Name)) { throw new InvalidOperationException("Invalid saved recovery record."); } contents.Add(recoveredItem); } plugin.LogInfo($"Loaded {contents.Count} Lost and Found items."); } catch (Exception error) { loadFailed = true; plugin.Error("Lost and Found save could not be loaded; recovery is paused to preserve it", error); } } private void Save() { if (saveFile == null || loadFailed) { throw new InvalidOperationException("Recovery save is not ready."); } ES3.Save<string[]>("LostAndFound.Items.v1", ((IEnumerable<RecoveredItem>)contents).Select((Func<RecoveredItem, string>)JsonUtility.ToJson).ToArray(), saveFile); } private static HashSet<ulong> ExpandItems(IEnumerable<GrabbableObject> roots) { return InventoryTraversal.Expand(roots, (GrabbableObject item) => (!Object.op_Implicit((Object)(object)item) || !Object.op_Implicit((Object)(object)((NetworkBehaviour)item).NetworkObject) || !((NetworkBehaviour)item).NetworkObject.IsSpawned) ? ((ulong?)null) : new ulong?(((NetworkBehaviour)item).NetworkObjectId), delegate(GrabbableObject item) { BeltBagItem val = (BeltBagItem)(object)((item is BeltBagItem) ? item : null); return (val == null || val.objectsInBag == null) ? Enumerable.Empty<GrabbableObject>() : val.objectsInBag; }); } private static HashSet<ulong> ReadInventory(PlayerControllerB player) { return ExpandItems((player.ItemSlots ?? Array.Empty<GrabbableObject>()).Concat((IEnumerable<GrabbableObject>)(object)new GrabbableObject[1] { player.ItemOnlySlot })); } internal unsafe void CaptureDeath(PlayerControllerB player) { //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: 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_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)network) || !Object.op_Implicit((Object)(object)player)) { return; } if (!Host) { if (!((NetworkBehaviour)player).IsOwner || !network.IsConnectedClient || capturedDeaths.Contains(player.actualClientId)) { return; } capturedDeaths.Add(player.actualClientId); ulong[] array = ReadInventory(player).Take(4096).ToArray(); FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(4 + array.Length * 8, (Allocator)2, -1); try { int num = array.Length; ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref num, default(ForPrimitives)); ulong[] array2 = array; for (num = 0; num < array2.Length; num++) { ulong num2 = array2[num]; ((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref num2, default(ForPrimitives)); } network.CustomMessagingManager.SendNamedMessage("achachala.lostandfound.death.v1", 0uL, val, (NetworkDelivery)4); return; } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } if (deathReports.TryGetValue(player.actualClientId, out HashSet<ulong> value)) { claims.MarkDeath(player.actualClientId, value); deathReports.Remove(player.actualClientId); } if (!capturedDeaths.Contains(player.actualClientId)) { HashSet<ulong> hashSet = ReadInventory(player); if (inventory.TryGetValue(player.actualClientId, out HashSet<ulong> value2)) { hashSet.UnionWith(value2); } claims.MarkDeath(player.actualClientId, hashSet); capturedDeaths.Add(player.actualClientId); } } private void ReceiveDeath(ulong sender, FastBufferReader reader) { //IL_002c: 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_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) if (!Host || !network.ConnectedClients.ContainsKey(sender) || !((FastBufferReader)(ref reader)).TryBeginRead(4)) { return; } int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives)); if (num < 0 || num > 4096 || !((FastBufferReader)(ref reader)).TryBeginRead(num * 8)) { return; } HashSet<ulong> hashSet = new HashSet<ulong>(); ulong num2 = default(ulong); for (int i = 0; i < num; i++) { ((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num2, default(ForPrimitives)); if (network.SpawnManager.SpawnedObjects.TryGetValue(num2, out var value) && ((lastHolder.TryGetValue(num2, out var value2) && value2 == sender) || value.OwnerClientId == sender)) { hashSet.Add(num2); } } deathReports[sender] = hashSet; } internal void Observe() { StartOfRound instance = StartOfRound.Instance; if (!Host || !Object.op_Implicit((Object)(object)instance) || instance.allPlayerScripts == null) { return; } PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if (!Object.op_Implicit((Object)(object)val) || val.isPlayerDead || !val.isPlayerControlled) { continue; } HashSet<ulong> hashSet = ReadInventory(val); foreach (ulong item in hashSet) { claims.Recovered(item); lastHolder[item] = val.actualClientId; } inventory[val.actualClientId] = hashSet; } allPlayerScripts = instance.allPlayerScripts; foreach (PlayerControllerB val2 in allPlayerScripts) { if (Object.op_Implicit((Object)(object)val2) && val2.isPlayerDead) { CaptureDeath(val2); } } } internal void Recovered(NetworkObjectReference reference, ulong holder) { NetworkObject val = default(NetworkObject); if (!Host || !((NetworkObjectReference)(ref reference)).TryGet(ref val, (NetworkManager)null)) { return; } GrabbableObject component = ((Component)val).GetComponent<GrabbableObject>(); foreach (ulong item in ExpandItems((IEnumerable<GrabbableObject>)(object)new GrabbableObject[1] { component })) { claims.Recovered(item); lastHolder[item] = holder; } } private static bool Eligible(GrabbableObject item) { if (!Object.op_Implicit((Object)(object)item) || !Object.op_Implicit((Object)(object)item.itemProperties) || !Object.op_Implicit((Object)(object)item.itemProperties.spawnPrefab)) { return false; } if (!item.itemProperties.isScrap) { return true; } Terminal val = Object.FindObjectOfType<Terminal>(); if (Object.op_Implicit((Object)(object)val) && val.buyableItemsList != null) { return val.buyableItemsList.Contains(item.itemProperties); } return false; } private static RecoveredItem Snapshot(GrabbableObject item) { return new RecoveredItem { Name = item.itemProperties.itemName, Prefab = ((Object)item.itemProperties.spawnPrefab).name, ItemId = item.itemProperties.itemId, Data = item.GetItemDataToSave(), HasBattery = (item.insertedBattery != null), Charge = ((item.insertedBattery != null) ? item.insertedBattery.charge : 0f), ScrapValue = item.scrapValue, UsedUp = item.itemUsedUp }; } private Dictionary<ulong, BeltBagItem> BagParents() { Dictionary<ulong, BeltBagItem> dictionary = new Dictionary<ulong, BeltBagItem>(); foreach (NetworkObject value in network.SpawnManager.SpawnedObjects.Values) { if (!Object.op_Implicit((Object)(object)value) || !value.IsSpawned) { continue; } BeltBagItem component = ((Component)value).GetComponent<BeltBagItem>(); if (!Object.op_Implicit((Object)(object)component) || component.objectsInBag == null) { continue; } foreach (GrabbableObject item in component.objectsInBag) { if (Object.op_Implicit((Object)(object)item) && Object.op_Implicit((Object)(object)((NetworkBehaviour)item).NetworkObject) && ((NetworkBehaviour)item).NetworkObject.IsSpawned) { dictionary[((NetworkBehaviour)item).NetworkObjectId] = component; } } } return dictionary; } private static int BagDepth(ulong id, Dictionary<ulong, BeltBagItem> parents) { BeltBagItem value; return InventoryTraversal.Depth(id, (ulong key) => (!parents.TryGetValue(key, out value)) ? ((ulong?)null) : new ulong?(((NetworkBehaviour)value).NetworkObjectId)); } private static bool OwnerDead(ulong owner) { return StartOfRound.Instance.allPlayerScripts.Any((PlayerControllerB p) => Object.op_Implicit((Object)(object)p) && p.actualClientId == owner && p.isPlayerDead); } private bool CanRecoverAncestors(ulong id, Dictionary<ulong, BeltBagItem> parents, Dictionary<ulong, ulong> owners) { ulong value; return InventoryTraversal.AncestorsAllowed(id, (ulong key) => (!parents.TryGetValue(key, out value)) ? ((ulong?)null) : new ulong?(((NetworkBehaviour)value).NetworkObjectId), delegate(ulong parentId) { BeltBagItem val = ((IEnumerable<BeltBagItem>)parents.Values).FirstOrDefault((Func<BeltBagItem, bool>)((BeltBagItem bag) => Object.op_Implicit((Object)(object)bag) && ((NetworkBehaviour)bag).NetworkObjectId == parentId)); if (!Object.op_Implicit((Object)(object)val) || !((NetworkBehaviour)val).NetworkObject.IsSpawned || !owners.TryGetValue(parentId, out value) || !OwnerDead(value)) { return false; } bool held = !parents.ContainsKey(parentId) && (((GrabbableObject)val).isHeld || ((GrabbableObject)val).isHeldByEnemy || Object.op_Implicit((Object)(object)((GrabbableObject)val).playerHeldBy)); return claims.CanCollect(parentId, ownerDead: true, held, inShip: false, eligible: true); }); } internal void Collect() { if (!Host || !plugin.RecoveryEnabled || !Object.op_Implicit((Object)(object)StartOfRound.Instance)) { return; } EnsureLoaded(); if (saveFile == null || loadFailed) { return; } Observe(); int num = 0; Dictionary<ulong, BeltBagItem> parents = BagParents(); Dictionary<ulong, ulong> dictionary = claims.Snapshot().ToDictionary((KeyValuePair<ulong, ulong> pair) => pair.Key, (KeyValuePair<ulong, ulong> pair) => pair.Value); foreach (KeyValuePair<ulong, ulong> item2 in dictionary.OrderByDescending((KeyValuePair<ulong, ulong> pair) => BagDepth(pair.Key, parents))) { if (!network.SpawnManager.SpawnedObjects.TryGetValue(item2.Key, out var value) || !Object.op_Implicit((Object)(object)value)) { continue; } GrabbableObject component = ((Component)value).GetComponent<GrabbableObject>(); BeltBagItem value2; bool flag = parents.TryGetValue(item2.Key, out value2); if (!Object.op_Implicit((Object)(object)component) || !CanRecoverAncestors(item2.Key, parents, dictionary) || !claims.CanCollect(item2.Key, OwnerDead(item2.Value), !flag && (component.isHeld || component.isHeldByEnemy || Object.op_Implicit((Object)(object)component.playerHeldBy)), inShip: false, Eligible(component))) { continue; } BeltBagItem val = (BeltBagItem)(object)((component is BeltBagItem) ? component : null); if (val != null && val.objectsInBag != null) { val.objectsInBag.RemoveAll((GrabbableObject child) => !Object.op_Implicit((Object)(object)child)); if (val.objectsInBag.Count > 0) { plugin.LogInfo("Leaving belt bag in place because some contents could not be recovered."); continue; } } string itemName = component.itemProperties.itemName; try { RecoveredItem item = Snapshot(component); contents.Add(item); try { Save(); } catch { contents.Remove(item); throw; } int num2 = ((flag && Object.op_Implicit((Object)(object)value2) && value2.objectsInBag != null) ? value2.objectsInBag.IndexOf(component) : (-1)); if (num2 >= 0) { value2.objectsInBag.RemoveAt(num2); } try { value.Despawn(true); } catch { if (num2 >= 0 && Object.op_Implicit((Object)(object)value2) && Object.op_Implicit((Object)(object)component)) { value2.objectsInBag.Insert(Math.Min(num2, value2.objectsInBag.Count), component); } contents.Remove(item); Save(); throw; } claims.Recovered(item2.Key); num++; if (flag && Object.op_Implicit((Object)(object)value2)) { BroadcastBagRemove(((NetworkBehaviour)value2).NetworkObjectId, item2.Key); } } catch (Exception error) { plugin.Error("Could not collect " + itemName, error); } } if (num > 0) { plugin.LogInfo($"Lost and Found collected {num} items at departure; {contents.Count} awaiting retrieval."); } } private unsafe void BroadcastBagRemove(ulong bagId, ulong itemId) { //IL_0011: 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_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(16, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref bagId, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref itemId, default(ForPrimitives)); foreach (ulong connectedClientsId in network.ConnectedClientsIds) { if (connectedClientsId != network.LocalClientId) { network.CustomMessagingManager.SendNamedMessage("achachala.lostandfound.bag-remove.v1", connectedClientsId, val, (NetworkDelivery)3); } } } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } private void ReceiveBagRemove(ulong sender, FastBufferReader reader) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) if (Host || sender != 0L || !((FastBufferReader)(ref reader)).TryBeginRead(16)) { return; } ulong key = default(ulong); ((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref key, default(ForPrimitives)); ulong itemId = default(ulong); ((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref itemId, default(ForPrimitives)); if (!Object.op_Implicit((Object)(object)network) || !network.SpawnManager.SpawnedObjects.TryGetValue(key, out var value)) { return; } BeltBagItem component = ((Component)value).GetComponent<BeltBagItem>(); if (Object.op_Implicit((Object)(object)component) && component.objectsInBag != null) { component.objectsInBag.RemoveAll((GrabbableObject item) => !Object.op_Implicit((Object)(object)item) || ((NetworkBehaviour)item).NetworkObjectId == itemId); } } private static Item? Resolve(RecoveredItem record) { Item[] array = StartOfRound.Instance.allItemsList.itemsList.Where((Item i) => Object.op_Implicit((Object)(object)i) && Object.op_Implicit((Object)(object)i.spawnPrefab) && i.itemName == record.Name && ((Object)i.spawnPrefab).name == record.Prefab).ToArray(); if (array.Length == 1) { return array[0]; } return ((IEnumerable<Item>)array).FirstOrDefault((Func<Item, bool>)((Item i) => i.itemId == record.ItemId)); } internal void Empty(Vector3 outlet) { //IL_0070: 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_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) if (!Host) { return; } EnsureLoaded(); if (loadFailed || saveFile == null) { return; } int num = 0; RecoveredItem[] array = contents.ToArray(); foreach (RecoveredItem recoveredItem in array) { Item val = Resolve(recoveredItem); if (!Object.op_Implicit((Object)(object)val)) { plugin.LogInfo("Keeping unavailable item in Lost and Found: " + recoveredItem.Name); continue; } GameObject val2 = null; bool flag = false; try { Vector3 val3 = outlet + new Vector3((float)(num % 3 - 1) * 0.1f, (float)(num / 9) * 0.025f, (float)(num / 3 % 3) * 0.1f); val2 = Object.Instantiate<GameObject>(val.spawnPrefab, val3, Quaternion.Euler(val.restingRotation), StartOfRound.Instance.elevatorTransform); GrabbableObject component = val2.GetComponent<GrabbableObject>(); NetworkObject component2 = val2.GetComponent<NetworkObject>(); if (!Object.op_Implicit((Object)(object)component) || !Object.op_Implicit((Object)(object)component2)) { throw new InvalidOperationException("Recovered prefab lacks native item/network components."); } component.isInShipRoom = (component.isInElevator = true); component2.Spawn(false); RestoredItem restoredItem = new RestoredItem { Id = component2.NetworkObjectId, Ticket = Guid.NewGuid().ToString("N"), Item = recoveredItem, Position = StartOfRound.Instance.elevatorTransform.InverseTransformPoint(val3) }; contents.Remove(recoveredItem); try { Save(); } catch { contents.Add(recoveredItem); throw; } flag = true; num++; restored[restoredItem.Id] = restoredItem; pending[restoredItem.Id] = Time.unscaledTime + 0.2f; foreach (ulong connectedClientsId in network.ConnectedClientsIds) { if (connectedClientsId != network.LocalClientId) { Send(connectedClientsId, restoredItem); } } } catch (Exception error) { if (Object.op_Implicit((Object)(object)val2) && !flag) { NetworkObject component3 = val2.GetComponent<NetworkObject>(); if (Object.op_Implicit((Object)(object)component3) && component3.IsSpawned) { component3.Despawn(true); } else { Object.Destroy((Object)(object)val2); } } plugin.Error(flag ? ("Returned item but state broadcast failed: " + recoveredItem.Name) : ("Could not return item; kept in bin: " + recoveredItem.Name), error); } } plugin.LogInfo($"Lost and Found returned {num} items; {contents.Count} remaining."); } private unsafe void Send(ulong client, RestoredItem state) { //IL_0029: 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_0052: Unknown result type (might be due to invalid IL or missing references) byte[] bytes = Encoding.UTF8.GetBytes(JsonUtility.ToJson((object)state)); FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(bytes.Length + 4, (Allocator)2, -1); try { int num = bytes.Length; ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref num, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteBytesSafe(bytes, bytes.Length, 0); network.CustomMessagingManager.SendNamedMessage("achachala.lostandfound.restored.v1", client, val, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } internal void SendPending(ulong client) { //IL_00da: 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) if (!Host) { return; } KeyValuePair<ulong, RestoredItem>[] array = restored.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair<ulong, RestoredItem> keyValuePair = array[i]; if (!network.SpawnManager.SpawnedObjects.ContainsKey(keyValuePair.Key)) { restored.Remove(keyValuePair.Key); continue; } GrabbableObject component = ((Component)network.SpawnManager.SpawnedObjects[keyValuePair.Key]).GetComponent<GrabbableObject>(); if (Object.op_Implicit((Object)(object)component) && !component.isHeld && !Object.op_Implicit((Object)(object)component.playerHeldBy)) { RestoredItem state = new RestoredItem { Id = keyValuePair.Key, Ticket = keyValuePair.Value.Ticket, Item = Snapshot(component), Position = StartOfRound.Instance.elevatorTransform.InverseTransformPoint(((Component)component).transform.position) }; Send(client, state); } } } private void ReceiveRestored(ulong sender, FastBufferReader reader) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) if (Host || sender != 0L || !((FastBufferReader)(ref reader)).TryBeginRead(4)) { return; } int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives)); if (num < 1 || num > 16384 || !((FastBufferReader)(ref reader)).TryBeginRead(num)) { return; } byte[] bytes = new byte[num]; ((FastBufferReader)(ref reader)).ReadBytesSafe(ref bytes, num, 0); try { RestoredItem restoredItem = JsonUtility.FromJson<RestoredItem>(Encoding.UTF8.GetString(bytes)); if (restoredItem != null && restoredItem.Item != null && (!applied.TryGetValue(restoredItem.Id, out string value) || !(value == restoredItem.Ticket))) { restored[restoredItem.Id] = restoredItem; if (!pending.ContainsKey(restoredItem.Id)) { pending[restoredItem.Id] = Time.unscaledTime + 0.2f; } } } catch (Exception error) { plugin.Error("Invalid recovered item state", error); } } internal void ApplyPending() { //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Expected O, but got Unknown //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)network) || !Object.op_Implicit((Object)(object)StartOfRound.Instance)) { return; } KeyValuePair<ulong, float>[] array = pending.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair<ulong, float> keyValuePair = array[i]; if (Time.unscaledTime < keyValuePair.Value) { continue; } if (!network.SpawnManager.SpawnedObjects.TryGetValue(keyValuePair.Key, out var value)) { if (Time.unscaledTime > keyValuePair.Value + 30f) { pending.Remove(keyValuePair.Key); } continue; } GrabbableObject component = ((Component)value).GetComponent<GrabbableObject>(); if (!Object.op_Implicit((Object)(object)component)) { pending.Remove(keyValuePair.Key); continue; } try { RestoredItem restoredItem = restored[keyValuePair.Key]; RecoveredItem item = restoredItem.Item; component.LoadItemSaveData(item.Data); component.itemUsedUp = item.UsedUp; if (item.HasBattery) { component.insertedBattery = new Battery(item.Charge <= 0f, Mathf.Clamp01(item.Charge)); } component.SetScrapValue(item.ScrapValue); if (!component.isHeld && !Object.op_Implicit((Object)(object)component.playerHeldBy)) { ((Component)component).transform.SetParent(StartOfRound.Instance.elevatorTransform, true); ((Component)component).transform.localPosition = restoredItem.Position; component.parentObject = null; component.isInShipRoom = (component.isInElevator = true); component.isInFactory = false; component.FallToGround(false, true, default(Vector3)); } applied[keyValuePair.Key] = restoredItem.Ticket; } catch (Exception error) { plugin.Error("Could not restore all saved state for recovered item", error); } pending.Remove(keyValuePair.Key); } } internal void EndRound() { claims.Clear(); inventory.Clear(); capturedDeaths.Clear(); lastHolder.Clear(); deathReports.Clear(); } internal void Reset() { if (!Host) { return; } EnsureLoaded(); if (loadFailed) { return; } contents.Clear(); EndRound(); try { Save(); } catch (Exception error) { plugin.Error("Could not clear recovery after ship reset", error); } } internal void Detach() { if (Object.op_Implicit((Object)(object)network) && network.CustomMessagingManager != null) { network.CustomMessagingManager.UnregisterNamedMessageHandler("achachala.lostandfound.restored.v1"); network.CustomMessagingManager.UnregisterNamedMessageHandler("achachala.lostandfound.death.v1"); network.CustomMessagingManager.UnregisterNamedMessageHandler("achachala.lostandfound.bag-remove.v1"); } network = null; saveFile = null; contents.Clear(); EndRound(); restored.Clear(); pending.Clear(); applied.Clear(); loadFailed = false; } } [Serializable] public sealed class RestoredItem { public ulong Id; public string Ticket = ""; public RecoveredItem Item; public Vector3 Position; } [HarmonyPatch(typeof(PlayerControllerB), "KillPlayer")] internal static class LocalDeathPatch { [HarmonyPrefix] private static void Prefix(PlayerControllerB __instance) { Plugin.Instance.Recovery.CaptureDeath(__instance); } } [HarmonyPatch(typeof(PlayerControllerB), "KillPlayerClientRpc")] internal static class RemoteDeathPatch { [HarmonyPrefix] private static void Prefix(int playerId) { StartOfRound instance = StartOfRound.Instance; if (Object.op_Implicit((Object)(object)instance) && playerId >= 0 && playerId < instance.allPlayerScripts.Length) { Plugin.Instance.Recovery.CaptureDeath(instance.allPlayerScripts[playerId]); } } } [HarmonyPatch(typeof(PlayerControllerB), "DropAllHeldItems")] internal static class DeathDropPatch { [HarmonyPrefix] private static void Prefix(PlayerControllerB __instance) { if (__instance.isPlayerDead) { Plugin.Instance.Recovery.CaptureDeath(__instance); } } } [HarmonyPatch(typeof(StartOfRound), "ShipLeave")] internal static class DeparturePatch { [HarmonyPrefix] [HarmonyPriority(800)] private static void Prefix() { Plugin.Instance.Recovery.Collect(); } } [HarmonyPatch(typeof(RoundManager), "DespawnPropsAtEndOfRound")] internal static class CleanupPatch { [HarmonyPrefix] [HarmonyPriority(800)] private static void Prefix() { Plugin.Instance.Recovery.Collect(); } } [HarmonyPatch(typeof(StartOfRound), "ReviveDeadPlayers")] internal static class RevivalPatch { [HarmonyPostfix] private static void Postfix() { Plugin.Instance.Recovery.EndRound(); } } [HarmonyPatch(typeof(StartOfRound), "ResetShip")] internal static class ShipResetPatch { [HarmonyPrefix] private static void Prefix() { Plugin.Instance.Recovery.Reset(); } } [HarmonyPatch(typeof(PlayerControllerB), "GrabObjectClientRpc")] internal static class RecoveredPickupPatch { [HarmonyPostfix] private static void Postfix(PlayerControllerB __instance, bool grabValidated, NetworkObjectReference grabbedObject) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (grabValidated) { Plugin.Instance.Recovery.Recovered(grabbedObject, __instance.actualClientId); } } } internal sealed class RequestGate { private readonly Dictionary<ulong, (uint sequence, double time)> accepted = new Dictionary<ulong, (uint, double)>(); internal bool TryAccept(ulong client, uint sequence, double now, double cooldown) { if (sequence == 0 || double.IsNaN(now) || double.IsInfinity(now)) { return false; } if (accepted.TryGetValue(client, out (uint, double) value) && (sequence <= value.Item1 || now - value.Item2 < cooldown)) { return false; } accepted[client] = (sequence, now); return true; } internal void Remove(ulong client) { accepted.Remove(client); } internal void Clear() { accepted.Clear(); } } }