Decompiled source of VikingRail v0.46.1

plugins/VikingRail/VikingRail.dll

Decompiled 3 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.Rendering;
using UnityEngine.UI;
using VikingRail.Core;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("VikingRail")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Valheim railways with side-rail structural support, dirt trackbeds, portable vehicles, passenger seating, movement sounds and track snapping.")]
[assembly: AssemblyFileVersion("0.46.1.0")]
[assembly: AssemblyInformationalVersion("0.46.1")]
[assembly: AssemblyProduct("VikingRail")]
[assembly: AssemblyTitle("VikingRail")]
[assembly: AssemblyVersion("0.46.1.0")]
[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 VikingRail
{
	internal static class RailAudioClips
	{
		private static AudioClip? _handcart;

		private static AudioClip? _locomotive;

		private static AudioClip? _chuff;

		private static AudioClip? _hiss;

		private static AudioClip? _eitr;

		private static readonly MethodInfo? SetSamples = AccessTools.Method(typeof(AudioClip), "SetData", new Type[2]
		{
			typeof(float[]),
			typeof(int)
		}, (Type[])null);

		internal static AudioClip Chuff
		{
			get
			{
				if (!Object.op_Implicit((Object)(object)_chuff))
				{
					return _chuff = Create("VikingRail steam chuff", RailAudioSynthesis.GenerateSteamChuff());
				}
				return _chuff;
			}
		}

		internal static AudioClip Hiss
		{
			get
			{
				if (!Object.op_Implicit((Object)(object)_hiss))
				{
					return _hiss = Create("VikingRail steam hiss", RailAudioSynthesis.GenerateSteamHiss());
				}
				return _hiss;
			}
		}

		internal static AudioClip EitrHum
		{
			get
			{
				if (!Object.op_Implicit((Object)(object)_eitr))
				{
					return _eitr = Create("VikingRail Eitr resonance", RailAudioSynthesis.GenerateEitrHum());
				}
				return _eitr;
			}
		}

		internal static AudioClip Rolling(bool coal)
		{
			if (coal)
			{
				if (!Object.op_Implicit((Object)(object)_locomotive))
				{
					return _locomotive = Create("VikingRail locomotive wheels", RailAudioSynthesis.GenerateLocomotiveRoll());
				}
				return _locomotive;
			}
			if (!Object.op_Implicit((Object)(object)_handcart))
			{
				return _handcart = Create("VikingRail handcart wood and wheels", RailAudioSynthesis.GenerateHandcartRoll());
			}
			return _handcart;
		}

		private static AudioClip Create(string name, float[] samples)
		{
			AudioClip val = AudioClip.Create(name, samples.Length, 1, 22050, false);
			try
			{
				if (SetSamples != null && true.Equals(SetSamples.Invoke(val, new object[2] { samples, 0 })))
				{
					return val;
				}
				throw new InvalidOperationException("Could not load generated sound samples: " + name);
			}
			catch
			{
				Object.Destroy((Object)(object)val);
				throw;
			}
		}

		internal static AudioMixerGroup? FindEffectsMixer()
		{
			GameObject val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.GetPrefab("Cart") : null);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return null;
			}
			Vagon component = val.GetComponent<Vagon>();
			if (Object.op_Implicit((Object)(object)component) && component.m_wheelLoops != null)
			{
				ZSFX[] wheelLoops = component.m_wheelLoops;
				foreach (ZSFX val2 in wheelLoops)
				{
					AudioSource val3 = (Object.op_Implicit((Object)(object)val2) ? ((Component)val2).GetComponent<AudioSource>() : null);
					if (Object.op_Implicit((Object)(object)val3) && Object.op_Implicit((Object)(object)val3.outputAudioMixerGroup))
					{
						return val3.outputAudioMixerGroup;
					}
				}
			}
			AudioSource[] componentsInChildren = val.GetComponentsInChildren<AudioSource>(true);
			foreach (AudioSource val4 in componentsInChildren)
			{
				if (Object.op_Implicit((Object)(object)val4.outputAudioMixerGroup))
				{
					return val4.outputAudioMixerGroup;
				}
			}
			return null;
		}

		internal static void Cleanup()
		{
			if (Object.op_Implicit((Object)(object)_handcart))
			{
				Object.Destroy((Object)(object)_handcart);
			}
			if (Object.op_Implicit((Object)(object)_locomotive))
			{
				Object.Destroy((Object)(object)_locomotive);
			}
			if (Object.op_Implicit((Object)(object)_chuff))
			{
				Object.Destroy((Object)(object)_chuff);
			}
			if (Object.op_Implicit((Object)(object)_hiss))
			{
				Object.Destroy((Object)(object)_hiss);
			}
			if (Object.op_Implicit((Object)(object)_eitr))
			{
				Object.Destroy((Object)(object)_eitr);
			}
			_handcart = (_locomotive = (_chuff = (_hiss = (_eitr = null))));
		}
	}
	public sealed class RailAutoDrive : MonoBehaviour, Hoverable, Interactable
	{
		internal const string PrefabName = "vr_autocontroller";

		internal const string ControllerKey = "vikingrail_autodrive_controller";

		private const string ControllerZdoKey = "vikingrail_autodrive_controller_zdo";

		private const string EngineKey = "vikingrail_autodrive_engine";

		private const string EngineZdoKey = "vikingrail_autodrive_engine_zdo";

		internal static readonly Vector3 SocketOffset = new Vector3(-0.67f, 0.9f, -0.46f);

		internal static readonly Vector3 LocalBoundsCenter = new Vector3(0f, 0.31f, 0f);

		internal static readonly Vector3 LocalBoundsHalfSize = new Vector3(0.13f, 0.31f, 0.15f);

		private const float SocketRange = 2.5f;

		private static readonly HashSet<RailAutoDrive> Controllers = new HashSet<RailAutoDrive>();

		private static readonly HashSet<string> DestroyedIdentities = new HashSet<string>();

		private static ZDOMan? _trackedManager;

		private ZNetView _view;

		private Rigidbody? _body;

		private WearNTear? _wear;

		private Transform? _lever;

		private string _identity = "";

		private bool _started;

		private bool _pending;

		private float _requestTime;

		private long _requestOwner;

		private long _requestPlayer;

		internal bool IsReady
		{
			get
			{
				if (_started && Object.op_Implicit((Object)(object)_view) && _view.IsValid())
				{
					return _identity.Length != 0;
				}
				return false;
			}
		}

		internal ZNetView View => _view;

		internal string PersistentId => _identity;

		internal string EngineIdentity
		{
			get
			{
				if (!IsReady)
				{
					return "";
				}
				return _view.GetZDO().GetString("vikingrail_autodrive_engine", "");
			}
		}

		internal bool IsAttached => EngineIdentity.Length != 0;

		internal static IEnumerable<RailAutoDrive> LoadedControllers => Controllers;

		internal RailVehicle? Engine
		{
			get
			{
				RailVehicle railVehicle = FindEngine(EngineIdentity);
				if (!Object.op_Implicit((Object)(object)railVehicle) || !(railVehicle.AttachedAutoDriveId == PersistentId))
				{
					return null;
				}
				return railVehicle;
			}
		}

		internal static bool WasDestroyed(string identity)
		{
			if (identity.Length != 0)
			{
				return DestroyedIdentities.Contains(identity);
			}
			return false;
		}

		internal static RailAutoDrive? Find(string identity)
		{
			if (string.IsNullOrEmpty(identity))
			{
				return null;
			}
			foreach (RailAutoDrive controller in Controllers)
			{
				if (Object.op_Implicit((Object)(object)controller) && controller.IsReady && controller.PersistentId == identity)
				{
					return controller;
				}
			}
			return null;
		}

		private static RailVehicle? FindEngine(string identity)
		{
			if (identity.Length == 0)
			{
				return null;
			}
			foreach (RailVehicle loadedVehicle in RailVehicle.LoadedVehicles)
			{
				if (Object.op_Implicit((Object)(object)loadedVehicle) && loadedVehicle.IsReady && loadedVehicle.SupportsAutoDrive && loadedVehicle.PersistentId == identity)
				{
					return loadedVehicle;
				}
			}
			return null;
		}

		private void Awake()
		{
			_view = ((Component)this).GetComponent<ZNetView>();
			_body = ((Component)this).GetComponent<Rigidbody>();
			_wear = ((Component)this).GetComponent<WearNTear>();
			_lever = ((Component)this).transform.Find("visual/vr_auto_lever");
		}

		private void Start()
		{
			if (!_started && Object.op_Implicit((Object)(object)_view) && _view.IsValid())
			{
				_started = true;
				EnsureIdentity();
				EnsureDestructionTracking();
				Controllers.Add(this);
				_view.Register<long, ZDOID, bool>("VR_AutoAttach", (Action<long, long, ZDOID, bool>)RequestAttachment);
				_view.Register<bool, string>("VR_AutoAttachReply", (Action<long, bool, string>)ReceiveReply);
				if (Object.op_Implicit((Object)(object)_wear))
				{
					WearNTear? wear = _wear;
					wear.m_onDestroyed = (Action)Delegate.Combine(wear.m_onDestroyed, new Action(OnDestroyedPermanently));
				}
			}
		}

		private void EnsureIdentity()
		{
			_identity = _view.GetZDO().GetString("vikingrail_identity", "");
			if (_identity.Length == 0 && _view.IsOwner())
			{
				_identity = Guid.NewGuid().ToString("N");
				_view.GetZDO().Set("vikingrail_identity", _identity);
			}
		}

		private void Update()
		{
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			if (!_started || !Object.op_Implicit((Object)(object)_view) || !_view.IsValid())
			{
				return;
			}
			if (_identity.Length == 0)
			{
				EnsureIdentity();
			}
			if (_pending && Time.unscaledTime - _requestTime > 6f)
			{
				_pending = false;
				Tell("The controller request timed out. Check the attachment and try again.");
			}
			if (!IsReady)
			{
				return;
			}
			RailVehicle engine = Engine;
			if (Object.op_Implicit((Object)(object)engine) && engine.View.IsOwner())
			{
				if (!_view.IsOwner())
				{
					_view.ClaimOwnership();
				}
				if (_view.IsOwner())
				{
					_view.GetZDO().Set("vikingrail_autodrive_engine_zdo", engine.View.GetZDO().m_uid);
					engine.View.GetZDO().Set("vikingrail_autodrive_controller_zdo", _view.GetZDO().m_uid);
				}
			}
			if (_view.IsOwner() && IsAttached && DestroyedIdentities.Contains(EngineIdentity))
			{
				ClearEngine();
			}
			if (Object.op_Implicit((Object)(object)_lever))
			{
				_lever.localRotation = Quaternion.Euler((Object.op_Implicit((Object)(object)engine) && engine.AutoDriveActive) ? (-28f) : 28f, 0f, 0f);
			}
		}

		private void LateUpdate()
		{
			if (IsReady)
			{
				RailVehicle engine = Engine;
				if (Object.op_Implicit((Object)(object)engine))
				{
					Follow(engine);
				}
			}
		}

		internal void Follow(RailVehicle engine)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			if (IsReady && Object.op_Implicit((Object)(object)engine) && engine.IsReady && !(engine.AttachedAutoDriveId != PersistentId) && !(EngineIdentity != engine.PersistentId))
			{
				Vector3 val = ((Component)engine).transform.TransformPoint(SocketOffset);
				Quaternion rotation = ((Component)engine).transform.rotation;
				if (Object.op_Implicit((Object)(object)_body))
				{
					_body.position = val;
					_body.rotation = rotation;
				}
				((Component)this).transform.SetPositionAndRotation(val, rotation);
			}
		}

		public float GetHoverOffset()
		{
			return 0f;
		}

		public string GetHoverName()
		{
			return "Rail automatic controller";
		}

		public string GetHoverText()
		{
			RailVehicle engine = Engine;
			string text = ((!IsAttached) ? "Build beside a parked steam or Eitr engine" : (Object.op_Implicit((Object)(object)engine) ? engine.AutoDriveStatus : "Waiting for the attached engine to load"));
			string text2 = ((!IsAttached) ? "Attach controller" : ((Object.op_Implicit((Object)(object)engine) && engine.AutoDriveActive) ? "Stop automatic travel" : "Start automatic travel"));
			return Localization.instance.Localize(GetHoverName() + "\n" + text + "\n[<color=yellow>$KEY_Use</color>] " + text2 + (IsAttached ? "\n[<color=yellow>$KEY_AltPlace + $KEY_Use</color>] Detach when parked" : "\nRemove with the Hammer to recover its materials"));
		}

		public bool UseItem(Humanoid user, ItemData item)
		{
			return false;
		}

		public bool Interact(Humanoid user, bool hold, bool alt)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: 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_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			if (hold || !IsReady || _pending)
			{
				return false;
			}
			Player val = (Player)(object)((user is Player) ? user : null);
			if (!Object.op_Implicit((Object)(object)val) || (Object)(object)val != (Object)(object)Player.m_localPlayer || ((Character)val).IsDead() || ((Character)val).IsTeleporting())
			{
				return false;
			}
			if (Vector3.Distance(((Component)val).transform.position, ((Component)this).transform.position) > 4f || !PrivateArea.CheckAccess(((Component)this).transform.position, 0f, true, false))
			{
				return false;
			}
			RailVehicle railVehicle = (IsAttached ? FindEngine(EngineIdentity) : ClosestParkedEngine());
			if (!Object.op_Implicit((Object)(object)railVehicle))
			{
				Tell(IsAttached ? "Approach the linked engine so it is loaded before using the controller." : "Build the controller within 2.5 m of a parked steam or Eitr engine's left controls.");
				return false;
			}
			if (IsAttached && !alt)
			{
				if (railVehicle.AttachedAutoDriveId != PersistentId)
				{
					Tell("This controller's engine link is unavailable. Detach it before attaching again.");
					return false;
				}
				return railVehicle.RequestAutoDriveToggle(val);
			}
			if (!IsAttached && alt)
			{
				Tell("Remove the detached controller with the Hammer to recover its materials.");
				return false;
			}
			if (((Character)val).IsAttached() || val.GetDoodadController() != null)
			{
				Tell("Dismount before attaching or detaching the controller.");
				return false;
			}
			if (!railVehicle.AutoDriveAttachmentChangesAllowed)
			{
				Tell("Stop automatic travel, park the engine and dismount before changing its controller.");
				return false;
			}
			if (!NearSocket(railVehicle, val) || !PrivateArea.CheckAccess(((Component)railVehicle).transform.position, 0f, true, false) || !PrivateArea.CheckAccess(((Component)railVehicle).transform.TransformPoint(SocketOffset), 0f, true, false))
			{
				return false;
			}
			if (!_view.HasOwner())
			{
				Tell("Waiting for the controller's network owner. Try again shortly.");
				return false;
			}
			_pending = true;
			_requestTime = Time.unscaledTime;
			_requestOwner = _view.GetZDO().GetOwner();
			_requestPlayer = val.GetPlayerID();
			_view.InvokeRPC("VR_AutoAttach", new object[3]
			{
				_requestPlayer,
				railVehicle.View.GetZDO().m_uid,
				IsAttached
			});
			return true;
		}

		private RailVehicle? ClosestParkedEngine()
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: 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)
			RailVehicle result = null;
			float num = 2.5f;
			foreach (RailVehicle loadedVehicle in RailVehicle.LoadedVehicles)
			{
				if (Object.op_Implicit((Object)(object)loadedVehicle) && loadedVehicle.IsReady && loadedVehicle.SupportsAutoDrive && loadedVehicle.AutoDriveAttachmentChangesAllowed && loadedVehicle.AttachedAutoDriveId.Length == 0)
				{
					float num2 = Vector3.Distance(((Component)this).transform.position, ((Component)loadedVehicle).transform.TransformPoint(SocketOffset));
					if (!(num2 > num))
					{
						num = num2;
						result = loadedVehicle;
					}
				}
			}
			return result;
		}

		private bool NearSocket(RailVehicle engine, Player player)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: 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_001c: 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_0034: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = ((Component)engine).transform.TransformPoint(SocketOffset);
			if (Vector3.Distance(((Component)this).transform.position, val) <= 2.5f)
			{
				return Vector3.Distance(((Component)player).transform.position, val) <= 2.5f;
			}
			return false;
		}

		private void RequestAttachment(long sender, long playerId, ZDOID target, bool detach)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if (IsReady && _view.IsOwner())
			{
				string message;
				bool flag = TryAttachment(sender, playerId, target, detach, out message);
				_view.InvokeRPC(sender, "VR_AutoAttachReply", new object[2] { flag, message });
			}
		}

		private bool TryAttachment(long sender, long playerId, ZDOID target, bool detach, out string message)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: 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)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: 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_02b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0267: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fe: Unknown result type (might be due to invalid IL or missing references)
			message = "The controller is unavailable. Try again shortly.";
			if (!IsReady || !_view.IsOwner() || !RailSettings.Ready)
			{
				return false;
			}
			Player player = Player.GetPlayer(playerId);
			if (!Object.op_Implicit((Object)(object)player) || ((Character)player).IsDead() || ((Character)player).IsTeleporting() || ((Character)player).IsAttached() || player.GetDoodadController() != null || ((Character)player).GetOwner() != sender || Vector3.Distance(((Component)player).transform.position, ((Component)this).transform.position) > 4f)
			{
				message = "Stand on foot beside the controller before changing its attachment.";
				return false;
			}
			if (!RailWardAccess.Allows(((Component)this).transform.position, playerId))
			{
				message = "A ward prevents changing this controller.";
				return false;
			}
			RailVehicle railVehicle = null;
			foreach (RailVehicle loadedVehicle in RailVehicle.LoadedVehicles)
			{
				if (Object.op_Implicit((Object)(object)loadedVehicle) && loadedVehicle.IsReady && loadedVehicle.SupportsAutoDrive && loadedVehicle.View.GetZDO().m_uid == target)
				{
					railVehicle = loadedVehicle;
					break;
				}
			}
			if (!Object.op_Implicit((Object)(object)railVehicle) || !NearSocket(railVehicle, player))
			{
				message = "The controller and player must be beside a loaded steam or Eitr engine's left controls.";
				return false;
			}
			if (!railVehicle.AutoDriveAttachmentChangesAllowed)
			{
				message = "Stop automatic travel, park the engine and dismount before changing its controller.";
				return false;
			}
			if (!RailWardAccess.Allows(((Component)railVehicle).transform.position, playerId) || !RailWardAccess.Allows(((Component)railVehicle).transform.TransformPoint(SocketOffset), playerId))
			{
				message = "A ward protects the engine's controller socket.";
				return false;
			}
			bool num;
			if (!detach)
			{
				if (IsAttached)
				{
					goto IL_01a9;
				}
				num = railVehicle.AttachedAutoDriveId.Length != 0;
			}
			else
			{
				num = EngineIdentity != railVehicle.PersistentId;
			}
			if (!num)
			{
				if (!detach && !HasClearance(railVehicle))
				{
					message = "Clear the left control socket of people and other objects before attaching.";
					return false;
				}
				if (!detach || railVehicle.AttachedAutoDriveId == PersistentId)
				{
					railVehicle.View.ClaimOwnership();
					if (!railVehicle.View.IsOwner() || !railVehicle.AutoDriveAttachmentChangesAllowed || (!detach && railVehicle.AttachedAutoDriveId.Length != 0))
					{
						message = "The engine became unavailable. Try again once it is parked.";
						return false;
					}
					railVehicle.StopAutomaticDrive("");
					railVehicle.SetAutoDriveId(detach ? "" : PersistentId);
					railVehicle.View.GetZDO().Set("vikingrail_autodrive_controller_zdo", detach ? ZDOID.None : _view.GetZDO().m_uid);
				}
				_view.GetZDO().Set("vikingrail_autodrive_engine", detach ? "" : railVehicle.PersistentId);
				_view.GetZDO().Set("vikingrail_autodrive_engine_zdo", detach ? ZDOID.None : railVehicle.View.GetZDO().m_uid);
				if (!detach)
				{
					Follow(railVehicle);
				}
				ZDOMan.instance.ForceSendZDO(sender, railVehicle.View.GetZDO().m_uid);
				ZDOMan.instance.ForceSendZDO(sender, _view.GetZDO().m_uid);
				message = (detach ? "Controller detached. Remove it with the Hammer to recover its materials." : "Automatic controller attached. Fuel the engine, then use the lever to send it forward.");
				return true;
			}
			goto IL_01a9;
			IL_01a9:
			message = "The controller attachment changed. Check both pieces and try again.";
			return false;
		}

		private bool HasClearance(RailVehicle engine)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: 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_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			Collider[] array = Physics.OverlapBox(((Component)engine).transform.TransformPoint(SocketOffset + LocalBoundsCenter), LocalBoundsHalfSize - Vector3.one * 0.002f, ((Component)engine).transform.rotation, -1, (QueryTriggerInteraction)1);
			foreach (Collider val in array)
			{
				if (!((Object)(object)((Component)val).GetComponentInParent<RailAutoDrive>() == (Object)(object)this) && !((Object)(object)((Component)val).GetComponentInParent<RailVehicle>() == (Object)(object)engine))
				{
					return false;
				}
			}
			return true;
		}

		private void ReceiveReply(long sender, bool success, string message)
		{
			if (_pending && sender == _requestOwner)
			{
				_pending = false;
				if (Object.op_Implicit((Object)(object)Player.m_localPlayer) && Player.m_localPlayer.GetPlayerID() == _requestPlayer)
				{
					Tell(message);
				}
			}
		}

		internal void DetachFromDestroyedEngine(RailVehicle engine)
		{
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			if (!IsReady || !Object.op_Implicit((Object)(object)engine) || !engine.IsReady || !engine.View.IsOwner() || EngineIdentity != engine.PersistentId)
			{
				return;
			}
			_view.ClaimOwnership();
			if (_view.IsOwner())
			{
				Follow(engine);
				ClearEngine();
				if (engine.AttachedAutoDriveId == PersistentId)
				{
					engine.StopAutomaticDrive("");
					engine.SetAutoDriveId("");
					engine.View.GetZDO().Set("vikingrail_autodrive_controller_zdo", ZDOID.None);
				}
			}
		}

		private void ClearEngine()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			_view.GetZDO().Set("vikingrail_autodrive_engine", "");
			_view.GetZDO().Set("vikingrail_autodrive_engine_zdo", ZDOID.None);
		}

		private void OnDestroyedPermanently()
		{
			if (IsReady && _view.IsOwner())
			{
				TrackDestroyed(_view.GetZDO());
			}
		}

		private void OnDestroy()
		{
			if (Object.op_Implicit((Object)(object)_wear))
			{
				WearNTear? wear = _wear;
				wear.m_onDestroyed = (Action)Delegate.Remove(wear.m_onDestroyed, new Action(OnDestroyedPermanently));
			}
			Controllers.Remove(this);
			_started = false;
		}

		internal static void EnsureDestructionTracking()
		{
			if (_trackedManager != ZDOMan.instance && ZDOMan.instance != null)
			{
				if (_trackedManager != null)
				{
					ZDOMan? trackedManager = _trackedManager;
					trackedManager.m_onZDODestroyed = (Action<ZDO>)Delegate.Remove(trackedManager.m_onZDODestroyed, new Action<ZDO>(TrackDestroyed));
				}
				_trackedManager = ZDOMan.instance;
				DestroyedIdentities.Clear();
				ZDOMan? trackedManager2 = _trackedManager;
				trackedManager2.m_onZDODestroyed = (Action<ZDO>)Delegate.Combine(trackedManager2.m_onZDODestroyed, new Action<ZDO>(TrackDestroyed));
			}
		}

		internal static void Cleanup()
		{
			if (_trackedManager != null)
			{
				ZDOMan? trackedManager = _trackedManager;
				trackedManager.m_onZDODestroyed = (Action<ZDO>)Delegate.Remove(trackedManager.m_onZDODestroyed, new Action<ZDO>(TrackDestroyed));
			}
			_trackedManager = null;
			DestroyedIdentities.Clear();
			Controllers.Clear();
		}

		private static void TrackDestroyed(ZDO destroyed)
		{
			try
			{
				RepairDestroyedLinks(destroyed);
			}
			catch (Exception error)
			{
				RailPlugin.Error(error);
			}
		}

		private static void RepairDestroyedLinks(ZDO destroyed)
		{
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			if (destroyed == null)
			{
				return;
			}
			string text = destroyed.GetString("vikingrail_identity", "");
			if (text.Length == 0)
			{
				return;
			}
			DestroyedIdentities.Add(text);
			RailAutoDrive[] array = Controllers.ToArray();
			foreach (RailAutoDrive railAutoDrive in array)
			{
				if (Object.op_Implicit((Object)(object)railAutoDrive) && railAutoDrive.IsReady && railAutoDrive._view.IsOwner() && !(railAutoDrive.EngineIdentity != text))
				{
					railAutoDrive.ClearEngine();
				}
			}
			string text2 = destroyed.GetString("vikingrail_autodrive_engine", "");
			RailVehicle railVehicle = FindEngine(text2);
			if (Object.op_Implicit((Object)(object)railVehicle) && railVehicle.View.IsOwner() && railVehicle.AttachedAutoDriveId == text)
			{
				railVehicle.SetAutoDriveId("");
				railVehicle.StopAutomaticDrive("The automatic controller was destroyed.");
				railVehicle.View.GetZDO().Set("vikingrail_autodrive_controller_zdo", ZDOID.None);
			}
			if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer() || ZDOMan.instance == null)
			{
				return;
			}
			string text3 = destroyed.GetString("vikingrail_autodrive_controller", "");
			if (text3.Length != 0)
			{
				ZDO val = FindSavedLink(destroyed, "vikingrail_autodrive_controller_zdo", text3, "vikingrail_autodrive_engine", text);
				if (val != null)
				{
					val.SetOwner(ZDOMan.GetSessionID());
					val.Set("vikingrail_autodrive_engine", "");
					val.Set("vikingrail_autodrive_engine_zdo", ZDOID.None);
				}
			}
			if (text2.Length != 0 && !Object.op_Implicit((Object)(object)railVehicle))
			{
				ZDO val2 = FindSavedLink(destroyed, "vikingrail_autodrive_engine_zdo", text2, "vikingrail_autodrive_controller", text);
				if (val2 != null)
				{
					val2.SetOwner(ZDOMan.GetSessionID());
					val2.Set("vikingrail_autodrive_active", false);
					val2.Set("vikingrail_autodrive_owner", 0L);
					val2.Set("vikingrail_autodrive_owner_revision", -1);
					val2.Set("vikingrail_autodrive_status", "The automatic controller was destroyed.");
					val2.Set("vikingrail_autodrive_controller", "");
					val2.Set("vikingrail_autodrive_controller_zdo", ZDOID.None);
				}
			}
		}

		private static ZDO? FindSavedLink(ZDO origin, string cachedIdKey, string identity, string reverseKey, string reverseIdentity)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			ZDO zDO = ZDOMan.instance.GetZDO(origin.GetZDOID(cachedIdKey));
			if (Matches(zDO))
			{
				return zDO;
			}
			List<ZDO> list = new List<ZDO>();
			ZDOMan.instance.FindSectorObjects(origin.GetSector(), new SimulationDistance(1, 0, false), list, (List<ZDO>)null);
			foreach (ZDO item in list)
			{
				if (Matches(item))
				{
					return item;
				}
			}
			return null;
			bool Matches(ZDO? candidate)
			{
				if (candidate != null && candidate.GetString("vikingrail_identity", "") == identity)
				{
					return candidate.GetString(reverseKey, "") == reverseIdentity;
				}
				return false;
			}
		}

		private static void Tell(string message)
		{
			if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, message, 0, (Sprite)null, false);
			}
		}
	}
	[HarmonyPatch(typeof(Piece), "CanBeRemoved")]
	internal static class AttachedAutoDriveRemovalPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Piece __instance, ref bool __result)
		{
			RailAutoDrive component = ((Component)__instance).GetComponent<RailAutoDrive>();
			if (Object.op_Implicit((Object)(object)component) && component.IsAttached)
			{
				__result = false;
			}
		}
	}
	[HarmonyPatch(typeof(WearNTear), "RPC_Remove")]
	internal static class AttachedAutoDriveRemovalRequestPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(WearNTear __instance)
		{
			RailAutoDrive component = ((Component)__instance).GetComponent<RailAutoDrive>();
			if (Object.op_Implicit((Object)(object)component))
			{
				return !component.IsAttached;
			}
			return true;
		}
	}
	internal static class RailBuildMenu
	{
		private static readonly FieldInfo? Usage = AccessTools.Field(typeof(Piece), "m_usage");

		internal static void ConfigureTransportPiece(Piece piece)
		{
			if (Usage == null || Usage.IsStatic || !Usage.FieldType.IsEnum || !Enum.IsDefined(Usage.FieldType, "Transport"))
			{
				return;
			}
			try
			{
				Usage.SetValue(piece, Enum.Parse(Usage.FieldType, "Transport"));
			}
			catch (Exception ex)
			{
				RailPlugin.Warn("Eitr engine usage tag was unavailable; retaining its Misc category: " + ex.GetBaseException().Message);
			}
		}

		internal static void ConfigureRepairPiece(Piece piece)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			piece.m_category = (PieceCategory)1;
			if (Usage == null || Usage.IsStatic || !Usage.FieldType.IsEnum || !Enum.IsDefined(Usage.FieldType, "Crafting") || !Enum.IsDefined(Usage.FieldType, "Transport"))
			{
				return;
			}
			try
			{
				Usage.SetValue(piece, Enum.Parse(Usage.FieldType, "Crafting, Transport"));
			}
			catch (Exception ex)
			{
				RailPlugin.Warn("Repair cart usage tags were unavailable; retaining its Crafting category: " + ex.GetBaseException().Message);
			}
		}
	}
	[HarmonyPatch]
	internal static class RailBuildMenuRefreshPatch
	{
		private static readonly MethodInfo? Toggle = VoidMethod(typeof(Hud), "TogglePieceSelection");

		private static readonly MethodInfo? Known = VoidMethod(typeof(Player), "UpdateKnownRecipesList");

		private static readonly MethodInfo? Available = VoidMethod(typeof(Player), "UpdateAvailablePiecesList");

		private static readonly FieldInfo? Window = AccessTools.Field(typeof(Hud), "m_pieceSelectionWindow");

		private static readonly FieldInfo? BuildPieces = AccessTools.Field(typeof(Player), "m_buildPieces");

		private static bool _refreshing;

		private static bool _warned;

		private static MethodInfo? VoidMethod(Type type, string name)
		{
			MethodInfo methodInfo = AccessTools.Method(type, name, Type.EmptyTypes, (Type[])null);
			if (!(methodInfo != null) || methodInfo.IsStatic || !(methodInfo.ReturnType == typeof(void)))
			{
				return null;
			}
			return methodInfo;
		}

		[HarmonyPrepare]
		private static bool Prepare()
		{
			int num;
			if (Toggle != null && Known != null && Available != null && Window != null && !Window.IsStatic && Window.FieldType == typeof(GameObject) && BuildPieces != null && !BuildPieces.IsStatic)
			{
				num = ((BuildPieces.FieldType == typeof(PieceTable)) ? 1 : 0);
				if (num != 0)
				{
					goto IL_00a0;
				}
			}
			else
			{
				num = 0;
			}
			Warn("the native build-menu refresh API is unavailable on this game build.");
			goto IL_00a0;
			IL_00a0:
			return (byte)num != 0;
		}

		[HarmonyTargetMethod]
		private static MethodBase TargetMethod()
		{
			return Toggle;
		}

		[HarmonyPrefix]
		private static void Prefix(Hud __instance)
		{
			if (_refreshing || !Object.op_Implicit((Object)(object)__instance))
			{
				return;
			}
			try
			{
				object? value = Window.GetValue(__instance);
				GameObject val = (GameObject)((value is GameObject) ? value : null);
				Player localPlayer = Player.m_localPlayer;
				if (!Object.op_Implicit((Object)(object)val) || val.activeSelf || !Object.op_Implicit((Object)(object)localPlayer) || ((Character)localPlayer).IsDead() || !((Character)localPlayer).InPlaceMode() || !Object.op_Implicit((Object)(object)Game.instance) || !Object.op_Implicit((Object)(object)ObjectDB.instance) || !Object.op_Implicit((Object)(object)ZoneSystem.instance))
				{
					return;
				}
				object? value2 = BuildPieces.GetValue(localPlayer);
				PieceTable val2 = (PieceTable)((value2 is PieceTable) ? value2 : null);
				ItemData rightItem = ((Humanoid)localPlayer).GetRightItem();
				if (Object.op_Implicit((Object)(object)val2) && rightItem != null && rightItem.m_shared != null && !((Object)(object)rightItem.m_shared.m_buildPieces != (Object)(object)val2) && ((Humanoid)localPlayer).GetInventory().GetAllItems().Contains(rightItem))
				{
					_refreshing = true;
					if (RailPrefabs.HasDeferredWorkshopRecipes)
					{
						RailPrefabs.AddToHammer(ObjectDB.instance);
					}
					if (HasRegisteredRailRecipe(val2))
					{
						Known.Invoke(localPlayer, null);
						Available.Invoke(localPlayer, null);
					}
				}
			}
			catch (Exception ex)
			{
				Warn(ex.GetBaseException().Message);
			}
			finally
			{
				_refreshing = false;
			}
		}

		private static bool HasRegisteredRailRecipe(PieceTable table)
		{
			if (table.m_pieces == null)
			{
				return false;
			}
			foreach (GameObject piece in table.m_pieces)
			{
				if (!RailPrefabs.IsRegisteredBuildPiece(piece))
				{
					continue;
				}
				Piece component = piece.GetComponent<Piece>();
				if (!Object.op_Implicit((Object)(object)component) || !component.m_enabled || component.m_resources == null || component.m_resources.Length == 0)
				{
					continue;
				}
				bool flag = true;
				Requirement[] resources = component.m_resources;
				foreach (Requirement val in resources)
				{
					if (val == null || !Object.op_Implicit((Object)(object)val.m_resItem) || val.m_amount <= 0)
					{
						flag = false;
						break;
					}
				}
				if (flag)
				{
					return true;
				}
			}
			return false;
		}

		private static void Warn(string reason)
		{
			if (!_warned)
			{
				_warned = true;
				RailPlugin.Warn("Railway build-menu refresh skipped; normal build-menu behavior remains: " + reason);
			}
		}
	}
	internal static class RailCamera
	{
		internal static readonly MethodInfo? EyeMethod = AccessTools.Method(typeof(GameCamera), "GetOffsetedEyePos", Type.EmptyTypes, (Type[])null);

		internal static readonly MethodInfo? CollisionMethod = AccessTools.Method(typeof(GameCamera), "CollideRay2", new Type[3]
		{
			typeof(Vector3),
			typeof(Vector3),
			typeof(Vector3).MakeByRefType()
		}, (Type[])null);

		private static readonly FieldInfo? PlayerPosition = AccessTools.Field(typeof(GameCamera), "m_playerPos");

		private static readonly FieldInfo? PlayerVelocity = AccessTools.Field(typeof(GameCamera), "m_playerVel");

		private static bool _warned;

		private static Player? _player;

		private static Component? _rider;

		private static int _lastFrame = -1;

		private static Vector3 _lastPosition;

		internal static bool Prepare()
		{
			int num;
			if (EyeMethod != null && !EyeMethod.IsStatic && EyeMethod.ReturnType == typeof(Vector3) && CollisionMethod != null && !CollisionMethod.IsStatic && CollisionMethod.ReturnType == typeof(void) && PlayerPosition != null && !PlayerPosition.IsStatic && PlayerPosition.FieldType == typeof(Vector3) && PlayerVelocity != null && !PlayerVelocity.IsStatic)
			{
				num = ((PlayerVelocity.FieldType == typeof(Vector3)) ? 1 : 0);
				if (num != 0)
				{
					goto IL_00fa;
				}
			}
			else
			{
				num = 0;
			}
			if (!_warned)
			{
				_warned = true;
				RailPlugin.Warn("Rail camera smoothing is unavailable on this Valheim build; using the native camera.");
			}
			goto IL_00fa;
			IL_00fa:
			return (byte)num != 0;
		}

		internal static bool TryOffset(ref Vector3 smoothedPosition, ref Vector3 smoothedVelocity, out Vector3 offset)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: 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_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: 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_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: 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)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			offset = Vector3.zero;
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer) || ((Character)localPlayer).IsDead() || ((Character)localPlayer).IsTeleporting() || !((Character)localPlayer).IsAttached() || (Object)(object)localPlayer.GetAttachCameraPoint() != (Object)null)
			{
				return Reset();
			}
			IDoodadController doodadController = localPlayer.GetDoodadController();
			Component val = null;
			Transform anchor;
			if (doodadController is RailVehicle railVehicle && Object.op_Implicit((Object)(object)railVehicle) && (Object)(object)railVehicle == (Object)(object)RailVehicle.LocalDriven && railVehicle.IsReady)
			{
				Transform attachPoint = localPlayer.GetAttachPoint();
				if (Object.op_Implicit((Object)(object)attachPoint) && (Object)(object)((Component)attachPoint).GetComponentInParent<RailVehicle>() == (Object)(object)railVehicle)
				{
					val = (Component)(object)railVehicle;
				}
			}
			else if (doodadController is RailPassengerSeat railPassengerSeat && Object.op_Implicit((Object)(object)railPassengerSeat) && (Object)(object)railPassengerSeat == (Object)(object)RailPassengerSeat.LocalSeat)
			{
				if (railPassengerSeat.HasLocalRider(localPlayer))
				{
					val = (Component)(object)railPassengerSeat;
				}
			}
			else if (doodadController is RailPumpPartner railPumpPartner && Object.op_Implicit((Object)(object)railPumpPartner) && (Object)(object)railPumpPartner == (Object)(object)RailPumpPartner.LocalPartner && railPumpPartner.TryRider(localPlayer, out anchor))
			{
				val = (Component)(object)railPumpPartner;
			}
			if (!Object.op_Implicit((Object)(object)val))
			{
				return Reset();
			}
			Vector3 position = ((Component)localPlayer).transform.position;
			if (!((Object)(object)_player != (Object)(object)localPlayer) && !((Object)(object)_rider != (Object)(object)val) && Time.frameCount <= _lastFrame + 1)
			{
				Vector3 val2 = position - _lastPosition;
				if (!(((Vector3)(ref val2)).sqrMagnitude > 4f))
				{
					goto IL_016f;
				}
			}
			smoothedPosition = position;
			smoothedVelocity = Vector3.zero;
			goto IL_016f;
			IL_016f:
			_player = localPlayer;
			_rider = val;
			_lastFrame = Time.frameCount;
			_lastPosition = position;
			offset = smoothedPosition - position;
			return true;
		}

		private static bool Reset()
		{
			_player = null;
			_rider = null;
			_lastFrame = -1;
			return false;
		}
	}
	[HarmonyPatch]
	internal static class RailCameraTargetPatch
	{
		[HarmonyPrepare]
		private static bool Prepare()
		{
			return RailCamera.Prepare();
		}

		[HarmonyTargetMethod]
		private static MethodBase TargetMethod()
		{
			return RailCamera.EyeMethod;
		}

		[HarmonyPostfix]
		private static void Postfix(ref Vector3 ___m_playerPos, ref Vector3 ___m_playerVel, ref Vector3 __result)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			if (RailCamera.TryOffset(ref ___m_playerPos, ref ___m_playerVel, out var offset))
			{
				__result += offset;
			}
		}
	}
	[HarmonyPatch]
	internal static class RailCameraCollisionPatch
	{
		[HarmonyPrepare]
		private static bool Prepare()
		{
			return RailCamera.Prepare();
		}

		[HarmonyTargetMethod]
		private static MethodBase TargetMethod()
		{
			return RailCamera.CollisionMethod;
		}

		[HarmonyPrefix]
		private static void Prefix(ref Vector3 ___m_playerPos, ref Vector3 ___m_playerVel, ref Vector3 __0)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			if (RailCamera.TryOffset(ref ___m_playerPos, ref ___m_playerVel, out var offset))
			{
				__0 += offset;
			}
		}
	}
	internal sealed class RailCargoInventory : MonoBehaviour
	{
		private sealed class NativeRequestScope
		{
			internal RailCargoInventory State;

			internal long Peer;

			internal long Player;

			internal string Token = "";

			internal int Request;

			internal ContainerRpcResponse Response;
		}

		internal sealed class ReadScope
		{
			internal Inventory? Previous;

			internal bool Nested;
		}

		private static readonly MethodInfo? NativeLoad = AccessTools.Method(typeof(Container), "Load", Type.EmptyTypes, (Type[])null);

		private static readonly MethodInfo? CheckAccess = AccessTools.Method(typeof(Container), "CheckAccess", new Type[1] { typeof(long) }, (Type[])null);

		private static readonly FieldInfo? InventoryField = AccessTools.Field(typeof(Container), "m_inventory");

		private static readonly FieldInfo? LoadingField = AccessTools.Field(typeof(Container), "m_loading");

		private static readonly FieldInfo? RevisionField = AccessTools.Field(typeof(Container), "m_lastRevision");

		private static readonly FieldInfo? DataField = AccessTools.Field(typeof(Container), "m_lastDataString");

		private static readonly FieldInfo? WeightField = AccessTools.Field(typeof(Inventory), "m_totalWeight");

		private static readonly FieldInfo? WidthField = AccessTools.Field(typeof(Inventory), "m_width");

		private static readonly FieldInfo? HeightField = AccessTools.Field(typeof(Inventory), "m_height");

		private static readonly FieldInfo? CurrentContainer = AccessTools.Field(typeof(InventoryGui), "m_currentContainer");

		private static readonly FieldInfo? DragInventory = AccessTools.Field(typeof(InventoryGui), "m_dragInventory");

		private static readonly FieldInfo? SplitInventory = AccessTools.Field(typeof(InventoryGui), "m_splitInventory");

		private static readonly MethodInfo? ResetDrag = AccessTools.Method(typeof(InventoryGui), "SetupDragItem", new Type[3]
		{
			typeof(ItemData),
			typeof(Inventory),
			typeof(int)
		}, (Type[])null);

		private static readonly ConditionalWeakTable<Inventory, RailCargoInventory> Inventories = new ConditionalWeakTable<Inventory, RailCargoInventory>();

		private const float GrantTimeout = 5f;

		private static long _latestIntent;

		private static Inventory? _stagedLoad;

		private static Inventory? _readingInventory;

		[ThreadStatic]
		private static NativeRequestScope? _requestScope;

		private static int _requestSequence;

		private static readonly MethodInfo?[] NativeRequests = new MethodInfo[3]
		{
			AccessTools.Method(typeof(Container), "RPC_RequestOpen", new Type[2]
			{
				typeof(long),
				typeof(long)
			}, (Type[])null),
			AccessTools.Method(typeof(Container), "RPC_RequestStack", new Type[2]
			{
				typeof(long),
				typeof(long)
			}, (Type[])null),
			AccessTools.Method(typeof(Container), "RPC_RequestTakeAll", new Type[2]
			{
				typeof(long),
				typeof(long)
			}, (Type[])null)
		};

		private Container _container;

		private RailVehicle _cart;

		private ZNetView _view;

		private bool _nativeLoad;

		private string _error = "";

		private ContainerRpcResponse? _requested;

		private long _expectedPeer;

		private long _intent;

		private float _expires;

		private bool _granted;

		private bool _replaying;

		private long _loadedOwner;

		private ushort _loadedOwnerRevision;

		private uint _loadedDataRevision;

		private bool _hasLoadedOwner;

		private string _leaseToken = "";

		private RailVehicle? _leaseRoot;

		private bool _leaseAccepted;

		private bool _returning;

		private bool _requestSent;

		private int _requestNumber;

		private string _sealedToken = "";

		private float _nextRenew;

		private long _ownerRequestPeer;

		private long _ownerRequestPlayer;

		private string _ownerRequestToken = "";

		private int _ownerRequestOperation;

		private float _ownerRequestUntil;

		private bool _rpcRegistered;

		private bool LeaseUsable
		{
			get
			{
				if (_leaseAccepted && !_returning && _sealedToken != _leaseToken && Object.op_Implicit((Object)(object)Player.m_localPlayer) && Object.op_Implicit((Object)(object)_cart))
				{
					return _cart.CargoGrantMatches(((Character)Player.m_localPlayer).GetOwner(), Player.m_localPlayer.GetPlayerID(), _leaseToken);
				}
				return false;
			}
		}

		private bool GuiOpen
		{
			get
			{
				if (Object.op_Implicit((Object)(object)InventoryGui.instance))
				{
					object? obj = CurrentContainer?.GetValue(InventoryGui.instance);
					Container val = (Container)((obj is Container) ? obj : null);
					if (val != null)
					{
						return (Object)(object)val == (Object)(object)_container;
					}
				}
				return false;
			}
		}

		private void Awake()
		{
			_container = ((Component)this).GetComponent<Container>();
			_cart = ((Component)this).GetComponent<RailVehicle>();
			_view = ((Component)this).GetComponent<ZNetView>();
			RegisterRpc();
		}

		private void OnDestroy()
		{
			CloseUnsafeInventory();
			if (_leaseToken.Length != 0 && Object.op_Implicit((Object)(object)_cart) && _cart.IsReady)
			{
				_cart.EndCargoAccess(_leaseToken, _leaseRoot);
			}
		}

		private void RegisterRpc()
		{
			if (!_rpcRegistered && Object.op_Implicit((Object)(object)_view) && _view.IsValid())
			{
				_view.Register<long, string, int>("VR_CargoNative", (Action<long, long, string, int>)OwnerRequest);
				_view.Register<string, int, bool>("VR_CargoNativeReply", (Action<long, string, int, bool>)NativeReply);
				_rpcRegistered = true;
			}
		}

		private static RailCargoInventory Get(Container container)
		{
			return ((Component)container).GetComponent<RailCargoInventory>() ?? ((Component)container).gameObject.AddComponent<RailCargoInventory>();
		}

		internal static void Ensure(Container container)
		{
			Get(container).RegisterRpc();
		}

		internal static bool HasPending(RailVehicle cart)
		{
			RailCargoInventory component = ((Component)cart).GetComponent<RailCargoInventory>();
			if (Object.op_Implicit((Object)(object)component))
			{
				if (component._leaseToken.Length == 0)
				{
					return component._ownerRequestToken.Length != 0;
				}
				return true;
			}
			return false;
		}

		internal static bool HasLocalLease(RailVehicle cart, string token)
		{
			RailCargoInventory component = ((Component)cart).GetComponent<RailCargoInventory>();
			if (Object.op_Implicit((Object)(object)component) && component._leaseAccepted && component._leaseToken == token && !component._returning)
			{
				return (Object)(object)component._leaseRoot == (Object)(object)cart;
			}
			return false;
		}

		internal static bool HasConflictingPending(RailVehicle cart, string token)
		{
			RailCargoInventory component = ((Component)cart).GetComponent<RailCargoInventory>();
			if (Object.op_Implicit((Object)(object)component))
			{
				if (component._leaseToken.Length == 0 || !(component._leaseToken != token))
				{
					if (component._ownerRequestToken.Length != 0)
					{
						return component._ownerRequestToken != token;
					}
					return false;
				}
				return true;
			}
			return false;
		}

		internal static string InventoryDigest(ZDO data)
		{
			string text = data.GetString(ZDOVars.s_items, "");
			byte[] byteArray = data.GetByteArray(ZDOVars.s_items, (byte[])null);
			return RailCargoSnapshot.Digest("S" + text.Length + ":" + text + "B" + ((byteArray == null) ? "-" : Convert.ToBase64String(byteArray)));
		}

		internal static bool ValidRequester(Container container, long peer, long playerId, out Player player)
		{
			//IL_0042: 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_006c: Unknown result type (might be due to invalid IL or missing references)
			player = Player.GetPlayer(playerId);
			if (!Object.op_Implicit((Object)(object)container) || peer == 0L || playerId == 0L || !Object.op_Implicit((Object)(object)player) || ((Character)player).GetOwner() != peer || ((Character)player).IsDead() || ((Character)player).IsTeleporting() || Vector3.Distance(((Component)player).transform.position, ((Component)container).transform.position) >= 5f || (container.m_checkGuardStone && !RailWardAccess.Allows(((Component)container).transform.position, playerId)))
			{
				return false;
			}
			try
			{
				return CheckAccess != null && (bool)CheckAccess.Invoke(container, new object[1] { playerId });
			}
			catch (Exception ex)
			{
				RailPlugin.Warn("Cargo access validation failed: " + ex.GetBaseException().Message);
				return false;
			}
		}

		internal static bool TryRefresh(Container container, out string error, bool requireOwner = true)
		{
			error = "";
			if (!Object.op_Implicit((Object)(object)container))
			{
				error = "The cargo container is unavailable.";
				return false;
			}
			RailCargoInventory railCargoInventory = Get(container);
			if (!Object.op_Implicit((Object)(object)railCargoInventory._view) || !railCargoInventory._view.IsValid() || (requireOwner && !railCargoInventory._view.IsOwner()))
			{
				error = "Wait for the cargo owner and try again.";
				return false;
			}
			bool changed;
			bool result = railCargoInventory.LoadSafely(out changed);
			error = railCargoInventory._error;
			return result;
		}

		internal static bool TryRefreshForOwnership(Container container, out string error)
		{
			error = "";
			if (!Object.op_Implicit((Object)(object)container))
			{
				error = "The cargo container is unavailable.";
				return false;
			}
			RailCargoInventory railCargoInventory = Get(container);
			if (!Object.op_Implicit((Object)(object)railCargoInventory._view) || !railCargoInventory._view.IsValid() || !railCargoInventory._view.IsOwner())
			{
				error = "Wait for the cargo owner and try again.";
				return false;
			}
			ZDO zDO = railCargoInventory._view.GetZDO();
			if (railCargoInventory._hasLoadedOwner && railCargoInventory._loadedOwner == zDO.GetOwner() && railCargoInventory._loadedOwnerRevision == zDO.OwnerRevision)
			{
				error = railCargoInventory._error;
				return error.Length == 0;
			}
			return TryRefresh(container, out error);
		}

		internal static bool LoadPrefix(Container container, ref bool result)
		{
			if (!Object.op_Implicit((Object)(object)((Component)container).GetComponent<RailVehicle>()))
			{
				return true;
			}
			RailCargoInventory railCargoInventory = Get(container);
			if (railCargoInventory._nativeLoad)
			{
				return true;
			}
			railCargoInventory.LoadSafely(out result);
			return false;
		}

		private bool LoadSafely(out bool changed)
		{
			//IL_0251: Unknown result type (might be due to invalid IL or missing references)
			//IL_0258: Expected O, but got Unknown
			changed = false;
			if (!Object.op_Implicit((Object)(object)_view) || !_view.IsValid() || _container.GetInventory() == null)
			{
				return Fail("The cargo inventory is not ready.");
			}
			if (NativeLoad == null || InventoryField?.FieldType != typeof(Inventory) || LoadingField?.FieldType != typeof(bool) || RevisionField?.FieldType != typeof(uint) || WeightField?.FieldType != typeof(float) || WidthField?.FieldType != typeof(int) || HeightField?.FieldType != typeof(int) || CurrentContainer?.FieldType != typeof(Container))
			{
				return Fail("This Valheim build's native cargo loader is unavailable.");
			}
			Inventory inventory = _container.GetInventory();
			if ((bool)LoadingField.GetValue(_container))
			{
				return Fail("The cargo inventory is already loading.");
			}
			if (_error.Length == 0 && _hasLoadedOwner && _loadedDataRevision == _view.GetZDO().DataRevision && _loadedOwner == _view.GetZDO().GetOwner() && _loadedOwnerRevision == _view.GetZDO().OwnerRevision)
			{
				MarkLoadedOwner();
				return true;
			}
			List<ItemData> collection = new List<ItemData>(inventory.GetAllItems());
			float totalWeight = inventory.GetTotalWeight();
			int width = inventory.GetWidth();
			int height = inventory.GetHeight();
			object value = RevisionField.GetValue(_container);
			object value2 = DataField?.GetValue(_container);
			Inventory stagedLoad = _stagedLoad;
			bool forceDisableInit = ZNetView.m_forceDisableInit;
			try
			{
				Inventory val = new Inventory(_container.m_name, _container.m_bkg, width, height);
				val.GetAllItems().AddRange(collection);
				WeightField.SetValue(val, totalWeight);
				InventoryField.SetValue(_container, val);
				_stagedLoad = val;
				_nativeLoad = true;
				changed = (bool)NativeLoad.Invoke(_container, null);
				_nativeLoad = false;
				_stagedLoad = stagedLoad;
				InventoryField.SetValue(_container, inventory);
				if (changed)
				{
					LoadingField.SetValue(_container, true);
					inventory.GetAllItems().Clear();
					inventory.GetAllItems().AddRange(val.GetAllItems());
					WeightField.SetValue(inventory, val.GetTotalWeight());
					WidthField.SetValue(inventory, val.GetWidth());
					HeightField.SetValue(inventory, val.GetHeight());
					inventory.m_onChanged?.Invoke();
				}
				LoadingField.SetValue(_container, false);
				_error = "";
				MarkLoadedOwner();
				return true;
			}
			catch (Exception ex)
			{
				InventoryField.SetValue(_container, inventory);
				inventory.GetAllItems().Clear();
				inventory.GetAllItems().AddRange(collection);
				WeightField.SetValue(inventory, totalWeight);
				WidthField.SetValue(inventory, width);
				HeightField.SetValue(inventory, height);
				RevisionField.SetValue(_container, value);
				DataField?.SetValue(_container, value2);
				LoadingField.SetValue(_container, false);
				changed = false;
				return Fail("Cargo could not be loaded safely; its saved items have been kept. " + ex.GetBaseException().Message);
			}
			finally
			{
				_nativeLoad = false;
				_stagedLoad = stagedLoad;
				ZNetView.m_forceDisableInit = forceDisableInit;
			}
		}

		private bool Fail(string error)
		{
			if (_error != error)
			{
				RailPlugin.Error(new InvalidOperationException(error));
			}
			_error = error;
			CloseUnsafeInventory();
			return false;
		}

		private void MarkLoadedOwner()
		{
			Inventory inventory = _container.GetInventory();
			if (inventory != null && !Inventories.TryGetValue(inventory, out RailCargoInventory _))
			{
				Inventories.Add(inventory, this);
			}
			_loadedOwner = _view.GetZDO().GetOwner();
			_loadedOwnerRevision = _view.GetZDO().OwnerRevision;
			_loadedDataRevision = _view.GetZDO().DataRevision;
			_hasLoadedOwner = true;
		}

		private void CloseUnsafeInventory()
		{
			InventoryGui instance = InventoryGui.instance;
			if (Object.op_Implicit((Object)(object)instance))
			{
				object? obj = CurrentContainer?.GetValue(instance);
				Container val = (Container)((obj is Container) ? obj : null);
				if (val != null && (Object)(object)val == (Object)(object)_container)
				{
					instance.Hide();
				}
			}
		}

		internal static bool AllowShow(Container? container)
		{
			if (Object.op_Implicit((Object)(object)container))
			{
				RailMountAccess.Remember(container);
				if (RailMountAccess.Locked((Component)(object)container))
				{
					return false;
				}
			}
			if (!Object.op_Implicit((Object)(object)container) || !Object.op_Implicit((Object)(object)((Component)container).GetComponent<RailVehicle>()))
			{
				return true;
			}
			RailCargoInventory railCargoInventory = Get(container);
			if (railCargoInventory.CanUse() && railCargoInventory.LeaseUsable && TryRefresh(container, out string _))
			{
				return true;
			}
			railCargoInventory.CloseUnsafeInventory();
			return false;
		}

		internal static void GuardOpenInventory(InventoryGui gui)
		{
			object? obj = CurrentContainer?.GetValue(gui);
			Container val = (Container)((obj is Container) ? obj : null);
			if (val == null || !Object.op_Implicit((Object)(object)val))
			{
				return;
			}
			RailMountAccess.Remember(val);
			if (RailMountAccess.Locked((Component)(object)val))
			{
				gui.Hide();
			}
			else if (Object.op_Implicit((Object)(object)((Component)val).GetComponent<RailVehicle>()))
			{
				RailCargoInventory railCargoInventory = Get(val);
				if (!railCargoInventory.CanUse() || !railCargoInventory.LeaseUsable || !Object.op_Implicit((Object)(object)railCargoInventory._view) || !railCargoInventory._view.IsValid() || !railCargoInventory._view.IsOwner() || railCargoInventory._error.Length != 0)
				{
					gui.Hide();
				}
			}
		}

		internal static bool AllowGuiMutation(InventoryGui gui, InventoryGrid? grid = null, bool checkSplit = false)
		{
			Inventory inventory = (Object.op_Implicit((Object)(object)grid) ? grid.GetInventory() : null);
			object? obj = CurrentContainer?.GetValue(gui);
			Container val = (Container)((obj is Container) ? obj : null);
			object? obj2 = DragInventory?.GetValue(gui);
			Inventory inventory2 = (Inventory)((obj2 is Inventory) ? obj2 : null);
			Inventory inventory3 = (Inventory)(checkSplit ? /*isinst with value type is only supported in some contexts*/: null);
			if (CanMutateGuiInventory(gui, inventory) && CanMutateGuiInventory(gui, Object.op_Implicit((Object)(object)val) ? val.GetInventory() : null) && CanMutateGuiInventory(gui, inventory2))
			{
				return CanMutateGuiInventory(gui, inventory3);
			}
			return false;
		}

		private static bool CanMutateGuiInventory(InventoryGui gui, Inventory? inventory)
		{
			if (!RailMountAccess.CanMutate(inventory))
			{
				if (DragInventory?.GetValue(gui) == inventory)
				{
					try
					{
						ResetDrag?.Invoke(gui, new object[3] { null, null, 1 });
					}
					catch (Exception ex)
					{
						RailPlugin.Warn("Could not clear a stale mounted-object drag: " + ex.GetBaseException().Message);
					}
				}
				gui.Hide();
				return false;
			}
			if (inventory == null || !Inventories.TryGetValue(inventory, out RailCargoInventory value))
			{
				return true;
			}
			if (!Object.op_Implicit((Object)(object)value) || !Object.op_Implicit((Object)(object)value._container) || !value.GuiOpen || !value.CanUse() || !value.LeaseUsable || !value._view.IsOwner() || value._error.Length != 0)
			{
				if (DragInventory?.GetValue(gui) == inventory)
				{
					try
					{
						ResetDrag?.Invoke(gui, new object[3] { null, null, 1 });
					}
					catch (Exception ex2)
					{
						RailPlugin.Warn("Could not clear a stale cargo drag: " + ex2.GetBaseException().Message);
					}
				}
				if (Object.op_Implicit((Object)(object)value))
				{
					value.CloseUnsafeInventory();
				}
				return false;
			}
			return true;
		}

		internal static bool AllowSave(Container container)
		{
			if (Object.op_Implicit((Object)(object)((Component)container).GetComponent<RailCargoInventory>()))
			{
				return ((Component)container).GetComponent<RailCargoInventory>()._error.Length == 0;
			}
			return true;
		}

		internal static ReadScope? BeginRead(Inventory inventory)
		{
			if (inventory != _stagedLoad)
			{
				return null;
			}
			ReadScope result = new ReadScope
			{
				Previous = _readingInventory,
				Nested = (_readingInventory == inventory)
			};
			_readingInventory = inventory;
			return result;
		}

		internal static void EndRead(ReadScope? scope)
		{
			if (scope != null)
			{
				_readingInventory = scope.Previous;
			}
		}

		internal static void CheckLoaded(Inventory inventory, ZPackage package, ReadScope? scope)
		{
			if (scope == null || scope.Nested)
			{
				return;
			}
			if (package.GetPos() != package.Size())
			{
				throw new FormatException("The native cargo payload was not completely read.");
			}
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem == null || !Object.op_Implicit((Object)(object)allItem.m_dropPrefab) || allItem.m_stack <= 0)
				{
					throw new FormatException("A cargo item could not be loaded.");
				}
			}
		}

		internal static void CheckItemAdded(Inventory inventory, bool success)
		{
			if (!success && inventory == _stagedLoad && inventory == _readingInventory)
			{
				throw new FormatException("A cargo item could not be loaded. Restore its item mod before using this cart.");
			}
		}

		internal static bool BeginRequest(Container container, ContainerRpcResponse response)
		{
			if (!Object.op_Implicit((Object)(object)((Component)container).GetComponent<RailVehicle>()))
			{
				return true;
			}
			RailCargoInventory railCargoInventory = Get(container);
			if (!railCargoInventory.CanUse())
			{
				string reason = "Stop the train before opening storage.";
				if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
				{
					railCargoInventory._cart.CanRequestCargo(Player.m_localPlayer, out reason);
				}
				railCargoInventory.Notify(reason);
				return false;
			}
			if (railCargoInventory._returning || railCargoInventory._requested != null)
			{
				return false;
			}
			railCargoInventory._requested = response;
			railCargoInventory._intent = ++_latestIntent;
			railCargoInventory._expires = Time.unscaledTime + 5f;
			railCargoInventory._granted = false;
			railCargoInventory._requestSent = false;
			if (++_requestSequence >= 700000000)
			{
				_requestSequence = 1;
			}
			railCargoInventory._requestNumber = _requestSequence * 3 + Operation(response);
			if (railCargoInventory._leaseToken.Length == 0)
			{
				if (!railCargoInventory._cart.TryCargoRoot(out RailVehicle root))
				{
					railCargoInventory._requested = null;
					return false;
				}
				railCargoInventory._leaseToken = Guid.NewGuid().ToString("N");
				railCargoInventory._leaseRoot = root;
				railCargoInventory._leaseAccepted = false;
				railCargoInventory._cart.RequestCargoAccess(Player.m_localPlayer.GetPlayerID(), railCargoInventory._leaseToken);
			}
			railCargoInventory.DispatchRequest();
			return false;
		}

		private static int Operation(ContainerRpcResponse response)
		{
			if (response != CargoProtocol.Open)
			{
				if (response != CargoProtocol.Stack)
				{
					return 2;
				}
				return 1;
			}
			return 0;
		}

		private static ContainerRpcResponse ResponseFor(int request)
		{
			if (request % 3 != 0)
			{
				if (request % 3 != 1)
				{
					return CargoProtocol.TakeAll;
				}
				return CargoProtocol.Stack;
			}
			return CargoProtocol.Open;
		}

		internal static void LeaseReply(Container container, RailVehicle root, long sender, string token, bool granted, string reason)
		{
			RailCargoInventory railCargoInventory = Get(container);
			if (!((Object)(object)railCargoInventory._leaseRoot != (Object)(object)root) && !(railCargoInventory._leaseToken != token) && !railCargoInventory._returning && railCargoInventory._requested != null)
			{
				if (!granted)
				{
					railCargoInventory.Notify(reason);
					railCargoInventory.ClearLocalLease();
				}
				else
				{
					railCargoInventory._leaseAccepted = true;
					railCargoInventory.DispatchRequest();
				}
			}
		}

		private void DispatchRequest()
		{
			if (_requested != null && !_requestSent && CanUse() && LeaseUsable)
			{
				_requestSent = true;
				_expectedPeer = _view.GetZDO().GetOwner();
				_view.InvokeRPC("VR_CargoNative", new object[3]
				{
					Player.m_localPlayer.GetPlayerID(),
					_leaseToken,
					_requestNumber
				});
			}
		}

		private void OwnerRequest(long sender, long playerId, string token, int request)
		{
			if (Object.op_Implicit((Object)(object)_view) && _view.IsValid() && _view.IsOwner() && token != null && token.Length == 32 && Guid.TryParseExact(token, "N", out var _) && request >= 3 && request <= 2100000002 && !(token == _sealedToken) && _ownerRequestToken.Length == 0)
			{
				if (!ValidRequester(_container, sender, playerId, out Player player) || !_cart.CanRequestCargo(player, out string _))
				{
					_view.InvokeRPC(sender, "VR_CargoNativeReply", new object[3] { token, request, false });
				}
				else
				{
					_ownerRequestPeer = sender;
					_ownerRequestPlayer = playerId;
					_ownerRequestToken = token;
					_ownerRequestOperation = request;
					_ownerRequestUntil = Time.unscaledTime + 5f;
					ProcessOwnerRequest();
				}
			}
		}

		private void ProcessOwnerRequest()
		{
			if (_ownerRequestToken.Length == 0)
			{
				return;
			}
			Player player;
			string reason;
			if (!Object.op_Implicit((Object)(object)_view) || !_view.IsValid() || !_view.IsOwner())
			{
				_ownerRequestToken = "";
			}
			else if (Time.unscaledTime >= _ownerRequestUntil || _sealedToken == _ownerRequestToken || !ValidRequester(_container, _ownerRequestPeer, _ownerRequestPlayer, out player) || !_cart.CanRequestCargo(player, out reason))
			{
				_view.InvokeRPC(_ownerRequestPeer, "VR_CargoNativeReply", new object[3] { _ownerRequestToken, _ownerRequestOperation, false });
				_ownerRequestToken = "";
			}
			else
			{
				if (!_cart.CargoGrantMatches(_ownerRequestPeer, _ownerRequestPlayer, _ownerRequestToken))
				{
					return;
				}
				NativeRequestScope nativeRequestScope = new NativeRequestScope
				{
					State = this,
					Peer = _ownerRequestPeer,
					Player = _ownerRequestPlayer,
					Token = _ownerRequestToken,
					Request = _ownerRequestOperation,
					Response = ResponseFor(_ownerRequestOperation)
				};
				_ownerRequestToken = "";
				NativeRequestScope requestScope = _requestScope;
				try
				{
					_requestScope = nativeRequestScope;
					_cart.MarkCargoOrigin(nativeRequestScope.Token);
					MethodInfo? obj = NativeRequests[nativeRequestScope.Request % 3];
					if (obj == null)
					{
						throw new MissingMethodException("The native storage request is unavailable.");
					}
					obj.Invoke(_container, new object[2] { nativeRequestScope.Peer, nativeRequestScope.Player });
				}
				catch (Exception ex)
				{
					RailPlugin.Warn("Storage request failed safely: " + ex.GetBaseException().Message);
					_view.InvokeRPC(nativeRequestScope.Peer, "VR_CargoNativeReply", new object[3] { nativeRequestScope.Token, nativeRequestScope.Request, false });
				}
				finally
				{
					_requestScope = requestScope;
				}
			}
		}

		internal static bool TagNativeReply(ZNetView view, long peer, string name, object[] parameters)
		{
			NativeRequestScope requestScope = _requestScope;
			if (requestScope == null || (Object)(object)requestScope.State._view != (Object)(object)view || requestScope.Peer != peer || requestScope.Response.RpcName != name || parameters == null || parameters.Length != 1 || !(parameters[0] is bool flag))
			{
				return true;
			}
			view.InvokeRPC(peer, "VR_CargoNativeReply", new object[3] { requestScope.Token, requestScope.Request, flag });
			return false;
		}

		internal static bool AllowOwnerRequest(Container container, long peer, long playerId, ContainerRpcResponse response)
		{
			if (!Object.op_Implicit((Object)(object)((Component)container).GetComponent<RailVehicle>()))
			{
				return true;
			}
			NativeRequestScope requestScope = _requestScope;
			RailCargoInventory railCargoInventory = Get(container);
			if (requestScope == null || (Object)(object)requestScope.State != (Object)(object)railCargoInventory || requestScope.Peer != peer || requestScope.Player != playerId || requestScope.Response != response)
			{
				return false;
			}
			Player player;
			string error;
			bool flag = Object.op_Implicit((Object)(object)railCargoInventory._view) && railCargoInventory._view.IsValid() && railCargoInventory._view.IsOwner() && railCargoInventory._sealedToken != requestScope.Token && ValidRequester(container, peer, playerId, out player) && railCargoInventory._cart.CanRequestCargo(player, out error) && railCargoInventory._cart.CargoGrantMatches(peer, playerId, requestScope.Token) && (response != CargoProtocol.TakeAll || railCargoInventory._view.GetZDO().GetOwner() == peer) && TryRefresh(container, out error);
			if (!flag && Object.op_Implicit((Object)(object)railCargoInventory._view) && railCargoInventory._view.IsValid())
			{
				railCargoInventory._view.InvokeRPC(peer, response.RpcName, new object[1] { false });
			}
			return flag;
		}

		internal static bool Response(Container container, long peer, bool granted, ContainerRpcResponse response)
		{
			if (Object.op_Implicit((Object)(object)((Component)container).GetComponent<RailVehicle>()))
			{
				return Get(container)._replaying;
			}
			return true;
		}

		private void NativeReply(long peer, string token, int request, bool granted)
		{
			if (_requested != null && _requestSent && !(token != _leaseToken) && request == _requestNumber && ResponseFor(request) == _requested && _expectedPeer == peer && _intent == _latestIntent && !_returning && !(Time.unscaledTime >= _expires))
			{
				if (!granted)
				{
					Notify("Storage is busy or access was denied. Stop the train and try again.");
					ReturnLease();
				}
				else
				{
					_granted = true;
					TryComplete();
				}
			}
		}

		internal static void Seal(Container container, string token)
		{
			RailCargoInventory railCargoInventory = Get(container);
			railCargoInventory._sealedToken = token;
			railCargoInventory._ownerRequestToken = "";
			if (railCargoInventory._leaseToken == token)
			{
				railCargoInventory._requested = null;
				railCargoInventory._returning = true;
			}
			railCargoInventory.CloseUnsafeInventory();
			container.SetInUse(false);
		}

		private bool CanUse()
		{
			Player localPlayer = Player.m_localPlayer;
			string reason;
			if (Object.op_Implicit((Object)(object)_cart) && _cart.IsReady && Object.op_Implicit((Object)(object)_view) && _view.IsValid() && Object.op_Implicit((Object)(object)localPlayer) && ValidRequester(_container, ((Character)localPlayer).GetOwner(), localPlayer.GetPlayerID(), out Player _))
			{
				return _cart.CanRequestCargo(localPlayer, out reason);
			}
			return false;
		}

		private void Notify(string message)
		{
			if (Object.op_Implicit((Object)(object)Player.m_localPlayer) && message.Length != 0)
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, message, 0, (Sprite)null, false);
			}
		}

		private void ReturnLease()
		{
			_requested = null;
			_granted = false;
			_returning = true;
			CloseUnsafeInventory();
			if (Object.op_Implicit((Object)(object)_cart) && _cart.IsReady && _leaseToken.Length != 0)
			{
				_cart.EndCargoAccess(_leaseToken, _leaseRoot);
			}
		}

		private void ClearLocalLease()
		{
			_requested = null;
			_leaseToken = "";
			_leaseRoot = null;
			_leaseAccepted = false;
			_returning = false;
			_requestSent = false;
			_granted = false;
		}

		private void Update()
		{
			RegisterRpc();
			ProcessOwnerRequest();
			if (_leaseToken.Length == 0)
			{
				return;
			}
			bool flag = Object.op_Implicit((Object)(object)_cart) && _cart.CargoTokenMatches(_leaseToken);
			if (_returning)
			{
				CloseUnsafeInventory();
				if (!flag && (_leaseAccepted || Time.unscaledTime >= _expires))
				{
					ClearLocalLease();
				}
				return;
			}
			if (!CanUse() || (_requested != null && (_intent != _latestIntent || Time.unscaledTime >= _expires)))
			{
				Notify("Storage access ended. Stop the train and try again.");
				ReturnLease();
				return;
			}
			if (!LeaseUsable)
			{
				if (_requested == null || Time.unscaledTime >= _expires)
				{
					ReturnLease();
				}
				return;
			}
			if (Time.unscaledTime >= _nextRenew)
			{
				_nextRenew = Time.unscaledTime + 1f;
				_cart.RenewCargoAccess(_leaseToken);
			}
			DispatchRequest();
			if (_granted)
			{
				TryComplete();
			}
			if (_requested == null && !GuiOpen)
			{
				ReturnLease();
			}
		}

		private void TryComplete()
		{
			if (_requested == null || !_granted || !CanUse() || !LeaseUsable || !_view.IsOwner())
			{
				return;
			}
			ContainerRpcResponse requested = _requested;
			_requested = null;
			_granted = false;
			if (!TryRefresh(_container, out string error))
			{
				Notify(error);
				ReturnLease();
				return;
			}
			try
			{
				_replaying = true;
				requested.Method.Invoke(_container, new object[2] { _expectedPeer, true });
			}
			catch (Exception ex)
			{
				RailPlugin.Error(ex.GetBaseException());
				ReturnLease();
			}
			finally
			{
				_replaying = false;
			}
			if (!GuiOpen)
			{
				ReturnLease();
			}
		}
	}
	[HarmonyPatch(typeof(Container), "Load")]
	internal static class RailCargoLoadPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(Container __instance, ref bool __result)
		{
			return RailCargoInventory.LoadPrefix(__instance, ref __result);
		}
	}
	[HarmonyPatch(typeof(ZNetView), "InvokeRPC", new Type[]
	{
		typeof(long),
		typeof(string),
		typeof(object[])
	})]
	internal static class RailCargoTaggedReplyPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(ZNetView __instance, long __0, string __1, object[] __2)
		{
			return RailCargoInventory.TagNativeReply(__instance, __0, __1, __2);
		}
	}
	[HarmonyPatch(typeof(Container), "Save")]
	internal static class RailCargoSavePatch
	{
		[HarmonyPrefix]
		private static bool Prefix(Container __instance)
		{
			return RailCargoInventory.AllowSave(__instance);
		}
	}
	[HarmonyPatch]
	internal static class RailCargoDeserializePatch
	{
		[HarmonyTargetMethods]
		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(Inventory), "Load", new Type[1] { typeof(ZPackage) }, (Type[])null);
			MethodInfo methodInfo = AccessTools.Method(typeof(Inventory), "Load", new Type[2]
			{
				typeof(ZPackage),
				typeof(bool)
			}, (Type[])null);
			if (methodInfo != null)
			{
				yield return methodInfo;
			}
		}

		[HarmonyPrefix]
		private static void Prefix(Inventory __instance, ZPackage __0, out RailCargoInventory.ReadScope? __state)
		{
			__state = RailCargoInventory.BeginRead(__instance);
		}

		[HarmonyPostfix]
		private static void Postfix(Inventory __instance, ZPackage __0, RailCargoInventory.ReadScope? __state)
		{
			RailCargoInventory.CheckLoaded(__instance, __0, __state);
		}

		[HarmonyFinalizer]
		private static void Finalizer(RailCargoInventory.ReadScope? __state)
		{
			RailCargoInventory.EndRead(__state);
		}
	}
	[HarmonyPatch]
	internal static class RailCargoLoadItemPatch
	{
		[HarmonyTargetMethods]
		private static IEnumerable<MethodBase> TargetMethods()
		{
			MethodInfo[] methods = typeof(Inventory).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (methodInfo.Name == "AddItem" && methodInfo.ReturnType == typeof(bool) && !methodInfo.ContainsGenericParameters)
				{
					yield return methodInfo;
				}
			}
		}

		[HarmonyPostfix]
		private static void Postfix(Inventory __instance, bool __result)
		{
			RailCargoInventory.CheckItemAdded(__instance, __result);
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Show", new Type[]
	{
		typeof(Container),
		typeof(int)
	})]
	internal static class RailCargoShowPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(Container __0)
		{
			return RailCargoInventory.AllowShow(__0);
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Update")]
	internal static class RailCargoOpenInventoryPatch
	{
		[HarmonyPrefix]
		private static void Prefix(InventoryGui __instance)
		{
			RailCargoInventory.GuardOpenInventory(__instance);
		}
	}
	[HarmonyPatch]
	internal static class RailCargoGridInteractionPatch
	{
		[HarmonyTargetMethods]
		private static IEnumerable<MethodBase> TargetMethods()
		{
			string[] array = new string[3] { "OnSelectedItem", "OnRightClickItem", "OnReleasedItem" };
			foreach (string text in array)
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(InventoryGui), text, (Type[])null, (Type[])null);
				if (methodInfo != null && methodInfo.GetParameters().Length != 0 && methodInfo.GetParameters()[0].ParameterType == typeof(InventoryGrid))
				{
					yield return methodInfo;
				}
			}
		}

		[HarmonyPrefix]
		private static bool Prefix(InventoryGui __instance, InventoryGrid __0)
		{
			return RailCargoInventory.AllowGuiMutation(__instance, __0);
		}
	}
	[HarmonyPatch]
	internal static class RailCargoGuiActionPatch
	{
		[HarmonyTargetMethods]
		private static IEnumerable<MethodBase> TargetMethods()
		{
			string[] array = new string[4] { "OnTakeAll", "OnStackAll", "OnDropOutside", "OnSplitOk" };
			foreach (string text in array)
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(InventoryGui), text, Type.EmptyTypes, (Type[])null);
				if (methodInfo != null)
				{
					yield return methodInfo;
				}
			}
		}

		[HarmonyPrefix]
		private static bool Prefix(InventoryGui __instance, MethodBase __originalMethod)
		{
			return RailCargoInventory.AllowGuiMutation(__instance, null, __originalMethod.Name == "OnSplitOk");
		}
	}
	internal sealed class RailCargoVisual : MonoBehaviour
	{
		private const int SlotCount = 16;

		private const int MaxRenderers = 24;

		private const int MaxTransformDepth = 32;

		private const int MaxVertices = 150000;

		private static readonly Vector3 ItemSpace = new Vector3(0.26f, 0.4f, 0.3f);

		private readonly GameObject?[] _models = (GameObject?[])(object)new GameObject[16];

		private readonly GameObject?[] _prefabs = (GameObject?[])(object)new GameObject[16];

		private readonly int[] _variants = new int[16];

		private readonly bool[] _occupied = new bool[16];

		private readonly ItemData?[] _items = (ItemData?[])(object)new ItemData[16];

		private Container _container;

		private RailVehicle _cart;

		private ZNetView _view;

		private Inventory? _inventory;

		private GameObject? _display;

		private Mesh? _boxMesh;

		private Material? _woodMaterial;

		private bool _dirty = true;

		private bool _warned;

		private float _nextPoll;

		private void Awake()
		{
			_container = ((Component)this).GetComponent<Container>();
			_cart = ((Component)this).GetComponent<RailVehicle>();
			_view = ((Component)this).GetComponent<ZNetView>();
		}

		private void Update()
		{
			if (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsDedicated())
			{
				((Behaviour)this).enabled = false;
				return;
			}
			if (!Object.op_Implicit((Object)(object)_container) || !Object.op_Implicit((Object)(object)_cart) || !_cart.IsReady || !Object.op_Implicit((Object)(object)_view) || !_view.IsValid())
			{
				if (Object.op_Implicit((Object)(object)_display))
				{
					_display.SetActive(false);
				}
				return;
			}
			if (Object.op_Implicit((Object)(object)_display) && !_display.activeSelf)
			{
				_display.SetActive(true);
			}
			if (!_dirty && Time.unscaledTime < _nextPoll)
			{
				return;
			}
			_nextPoll = Time.unscaledTime + 0.5f;
			_dirty = false;
			Inventory inventory = _container.GetInventory();
			if (inventory != _inventory)
			{
				if (_inventory != null)
				{
					Inventory? inventory2 = _inventory;
					inventory2.m_onChanged = (Action)Delegate.Remove(inventory2.m_onChanged, new Action(MarkCargoDirty));
				}
				_inventory = inventory;
				if (_inventory != null)
				{
					Inventory? inventory3 = _inventory;
					inventory3.m_onChanged = (Action)Delegate.Combine(inventory3.m_onChanged, new Action(MarkCargoDirty));
				}
			}
			if (_inventory == null)
			{
				return;
			}
			try
			{
				Refresh();
			}
			catch (Exception ex)
			{
				if (!_warned)
				{
					_warned = true;
					RailPlugin.Warn("A cargo item could not be displayed: " + ex.Message);
				}
			}
		}

		private void MarkCargoDirty()
		{
			_dirty = true;
		}

		private void OnDestroy()
		{
			if (_inventory != null)
			{
				Inventory? inventory = _inventory;
				inventory.m_onChanged = (Action)Delegate.Remove(inventory.m_onChanged, new Action(MarkCargoDirty));
			}
		}

		private void Refresh()
		{
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Expected O, but got Unknown
			//IL_025e: Unknown result type (might be due to invalid IL or missing references)
			Array.Clear(_items, 0, _items.Length);
			List<ItemData> allItems = _inventory.GetAllItems();
			foreach (ItemData item in allItems)
			{
				if (item != null && item.m_stack > 0)
				{
					int x = item.m_gridPos.x;
					int y = item.m_gridPos.y;
					if (x >= 0 && x < 4 && y >= 0 && y < 4 && _items[y * 4 + x] == null)
					{
						_items[y * 4 + x] = item;
					}
				}
			}
			foreach (ItemData item2 in allItems)
			{
				if (item2 != null && item2.m_stack > 0 && Array.IndexOf(_items, item2) < 0)
				{
					int num = Array.IndexOf(_items, null);
					if (num < 0)
					{
						break;
					}
					_items[num] = item2;
				}
			}
			for (int i = 0; i < 16; i++)
			{
				ItemData? obj = _items[i];
				GameObject val = obj?.m_dropPrefab;
				int num2 = obj?.m_variant ?? 0;
				bool flag = obj != null;
				if (_occupied[i] == flag && (Object)(object)_prefabs[i] == (Object)(object)val && _variants[i] == num2 && (!flag || Object.op_Implicit((Object)(object)_models[i])))
				{
					continue;
				}
				if (Object.op_Implicit((Object)(object)_models[i]))
				{
					_models[i].SetActive(false);
					Object.Destroy((Object)(object)_models[i]);
				}
				_models[i] = null;
				_occupied[i] = flag;
				_prefabs[i] = val;
				_variants[i] = num2;
				if (flag)
				{
					EnsureDisplay();
					GameObject val2 = new GameObject("cargo_slot_" + i);
					val2.SetActive(false);
					val2.layer = ((Component)this).gameObject.layer;
					val2.transform.SetParent(_display.transform, false);
					val2.transform.localPosition = new Vector3(((float)(i % 4) - 1.5f) * 0.3f, 1.08f, ((float)(i / 4) - 1.5f) * 0.35f);
					_models[i] = val2;
					bool flag2 = false;
					try
					{
						flag2 = Object.op_Implicit((Object)(object)val) && TryBuildItem(val, val2.transform);
					}
					catch (Exception)
					{
					}
					if (!flag2)
					{
						AddFallbackCrate(val2.transform);
					}
					val2.SetActive(true);
				}
			}
		}

		private void EnsureDisplay()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			if (!Object.op_Implicit((Object)(object)_display))
			{
				_display = new GameObject("vr_cargo_display");
				_display.SetActive(false);
				_display.layer = ((Component)this).gameObject.layer;
				_display.transform.SetParent(((Component)this).transform, false);
				Transform val = ((Component)this).transform.Find("visual/cargo_bed_floor");
				if (Object.op_Implicit((Object)(object)val))
				{
					MeshFilter component = ((Component)val).GetComponent<MeshFilter>();
					_boxMesh = ((component != null) ? component.sharedMesh : null);
					MeshRenderer component2 = ((Component)val).GetComponent<MeshRenderer>();
					_woodMaterial = ((component2 != null) ? ((Renderer)component2).sharedMaterial : null);
				}
				_display.SetActive(true);
			}
		}

		private bool TryBuildItem(GameObject prefab, Transform slot)
		{
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Expected O, but got Unknown
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0293: 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_02af: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0304: Unknown result type (might be due to invalid IL or missing references)
			//IL_0328: Unknown result type (might be due to invalid IL or missing references)
			//IL_034c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0353: Unknown result type (might be due to invalid IL or missing references)
			//IL_0366: Unknown result type (might be due to invalid IL or missing references)
			//IL_0372: Unknown result type (might be due to invalid IL or missing references)
			//IL_037e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0388: Unknown result type (might be due to invalid IL or missing references)
			//IL_038d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0394: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0203: Unknown result type (might be due to invalid IL or missing references)
			//IL_0208: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			//IL_022e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			Transform val = FindVisualRoot(prefab.transform);
			MeshRenderer[] componentsInChildren = ((Component)val).GetComponentsInChildren<MeshRenderer>(true);
			if (componentsInChildren.Length == 0 || componentsInChildren.Length > 96)
			{
				return false;
			}
			HashSet<Renderer> hashSet = LowerLodRenderers(val);
			List<MeshRenderer> list = new List<MeshRenderer>();
			long num = 0L;
			MeshRenderer[] array = componentsInChildren;
			foreach (MeshRenderer val2 in array)
			{
				if (!Object.op_Implicit((Object)(object)val2) || !((Renderer)val2).enabled || hashSet.Contains((Renderer)(object)val2) || !VisiblePart(((Component)val2).transform, val))
				{
					continue;
				}
				MeshFilter component = ((Component)val2).GetComponent<MeshFilter>();
				if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.sharedMesh) && ((Renderer)val2).sharedMaterials.Length != 0)
				{
					num += component.sharedMesh.vertexCount;
					if (num > 150000 || list.Count == 24)
					{
						return false;
					}
					list.Add(val2);
				}
			}
			if (list.Count == 0)
			{
				return false;
			}
			GameObject val3 = new GameObject("item_model");
			val3.SetActive(false);
			val3.layer = ((Component)this).gameObject.layer;
			val3.transform.SetParent(slot, false);
			bool flag = false;
			try
			{
				Dictionary<Transform, Transform> copies = new Dictionary<Transform, Transform> { [val] = val3.transform };
				Bounds val4 = default(Bounds);
				bool flag2 = false;
				foreach (MeshRenderer item in list)
				{
					Transform val5 = CopyTransform(((Component)item).transform, val, copies, 0);
					if (!Object.op_Implicit((Object)(object)val5))
					{
						return false;
					}
					Mesh sharedMesh = ((Component)item).GetComponent<MeshFilter>().sharedMesh;
					((Component)val5).gameObject.AddComponent<MeshFilter>().sharedMesh = sharedMesh;
					MeshRenderer obj = ((Component)val5).gameObject.AddComponent<MeshRenderer>();
					((Renderer)obj).sharedMaterials = ((Renderer)item).sharedMaterials;
					((Renderer)obj).shadowCastingMode = (ShadowCastingMode)0;
					((Renderer)obj).receiveShadows = true;
					Bounds bounds = sharedMesh.bounds;
					for (int j = -1; j <= 1; j += 2)
					{
						for (int k = -1; k <= 1; k += 2)
						{
							for (int l = -1; l <= 1; l += 2)
							{
								Vector3 val6 = val3.transform.InverseTransformPoint(val5.TransformPoint(((Bounds)(ref bounds)).center + Vector3.Scale(((Bounds)(ref bounds)).extents, new Vector3((float)j, (float)k, (float)l))));
								if (!Finite(val6))
								{
									return false;
								}
								if (!flag2)
								{
									val4 = new Bounds(val6, Vector3.zero);
									flag2 = true;
								}
								else
								{
									((Bounds)(ref val4)).Encapsulate(val6);
								}
							}
						}
					}
				}
				if (flag2 && Finite(((Bounds)(ref val4)).center) && Finite(((Bounds)(ref val4)).size))
				{
					Vector3 size = ((Bounds)(ref val4)).size;
					if (!(((Vector3)(ref size)).sqrMagnitude < 1E-07f))
					{
						float num2 = Mathf.Min(new float[3]
						{
							ItemSpace.x / Mathf.Max(((Bounds)(ref val4)).size.x, 1E-05f),
							ItemSpace.y / Mathf.Max(((Bounds)(ref val4)).size.y, 1E-05f),
							ItemSpace.z / Mathf.Max(((Bounds)(ref val4)).size.z, 1E-05f)
						});
						val3.transform.localScale = Vector3.one * num2;
						val3.transform.localPosition = -new Vector3(((Bounds)(ref val4)).center.x, ((Bounds)(ref val4)).min.y, ((Bounds)(ref val4)).center.z) * num2;
						val3.SetActive(true);
						flag = true;
						return true;
					}
				}
				return false;
			}
			finally
			{
				if (!flag)
				{
					Object.Destroy((Object)(object)val3);
				}
			}
		}

		private static Transform FindVisualRoot(Transform prefab)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			string[] array = new string[2] { "attach", "visual" };
			foreach (string b in array)
			{
				foreach (Transform item in prefab)
				{
					Transform val = item;
					if (string.Equals(((Object)val).name, b, StringComparison.OrdinalIgnoreCase) && Object.op_Implicit((Object)(object)((Component)val).GetComponentInChildren<MeshRenderer>(true)))
					{
						return val;
					}
				}
			}
			return prefab;
		}

		private static HashSet<Renderer> LowerLodRenderers(Transform source)
		{
			HashSet<Renderer> hashSet = new HashSet<Renderer>();
			HashSet<Renderer> hashSet2 = new HashSet<Renderer>();
			LODGroup[] componentsInChildren = ((Component)source).GetComponentsInChildren<LODGroup>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				LOD[] lODs = componentsInChildren[i].GetLODs();
				for (int j = 0; j < lODs.Length; j++)
				{
					Renderer[] renderers = lODs[j].renderers;
					foreach (Renderer val in renderers)
					{
						if (Object.op_Implicit((Object)(object)val))
						{
							((j == 0) ? hashSet2 : hashSet).Add(val);
						}
					}
				}
			}
			hashSet.ExceptWith(hashSet2);
			return hashSet;
		}

		private Transform? CopyTransform(Transform original, Transform source, Dictionary<Transform, Transform> copies, int depth)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			//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_009b: Unknown result type (might be due to invalid IL or missing references)
			if (copies.TryGetValue(original, out Transform value))
			{
				return value;
			}
			if (depth >= 32 || !Object.op_Implicit((Object)(object)original.parent) || (Object)(object)original == (Object)(object)source)
			{
				return null;
			}
			Transform val = CopyTransform(original.parent, source, copies, depth + 1);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return null;
			}
			GameObject val2 = new GameObject(((Object)original).name);
			val2.layer = ((Component)this).gameObject.layer;
			val2.transform.SetParent(val, false);
			val2.transform.localPosition = original.localPosition;
			val2.transform.localRotation = original.localRotation;
			val2.transform.localScale = original.localScale;
			copies.Add(original, val2.transform);
			return val2.transform;
		}

		private static bool VisiblePart(Transform part, Transform root)
		{
			Transform val = part;
			while (Object.op_Implicit((Object)(object)val) && (Object)(object)val != (Object)(object)root)
			{
				if (!((Component)val).gameObject.activeSelf)
				{
					return false;
				}
				val = val.parent;
			}
			return true;
		}

		private void AddFallbackCrate(Transform slot)
		{
			//IL_0020: 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)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)_boxMesh) && Object.op_Implicit((Object)(object)_woodMaterial))
			{
				GameObject val = new GameObject("cargo_crate")
				{
					layer = ((Component)this).gameObject.layer
				};
				val.transform.SetParent(slot, false);
				val.transform.localPosition = new Vector3(0f, 0.13f, 0f);
				val.transform.localScale = new Vector3(0.24f, 0.26f, 0.28f);
				val.AddComponent<MeshFilter>().sharedMesh = _boxMesh;
				MeshRenderer obj = val.AddComponent<MeshRenderer>();
				((Renderer)obj).sharedMaterial = _woodMaterial;
				((Renderer)obj).shadowCastingMode = (ShadowCastingMode)0;
			}
		}

		private static bool Finite(Vector3 value)
		{
			//IL_0000: 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_001a: Unknown result type (might be due to invalid IL or missing references)
			if (Finite(value.x) && Finite(value.y))
			{
				return Finite(value.z);
			}
			return false;
		}

		private static bool Finite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}
	}
	public sealed class RailCouplingLever : MonoBehaviour, Hoverable, Interactable
	{
		public bool Front = true;

		internal static readonly Vector3 SocketOffset = new Vector3(-0.975f, 0.88f, 0.85f);

		internal static readonly Vector3 RearSocketOffset = new Vector3(-0.975f, 0.88f, -1.15f);

		internal static readonly Vector3 TargetCenter = new Vector3(-0.035f, 0.115f, 0f);

		internal static readonly Vector3 HalfExtents = new Vector3(0.115f, 0.23f, 0.25f);

		internal static readonly Vector3 Offset = SocketOffset + TargetCenter;

		internal static readonly Vector3 RearOffset = RearSocketOffset + TargetCenter;

		private RailVehicle? _cart;

		private Transform? _arm;

		private bool? _shownCoupled;

		private void Awake()
		{
			_cart = ((Component)this).GetComponentInParent<RailVehicle>();
			_arm = ((Component)this).transform.Find("vr_release_arm");
			RefreshPose();
		}

		private void Update()
		{
			RefreshPose();
		}

		private void RefreshPose()
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			bool flag = Object.op_Implicit((Object)(object)_cart) && _cart.IsReady && _cart.HasCouplingAt(Front);
			if (Object.op_Implicit((Object)(object)_arm) && _shownCoupled != flag)
			{
				_arm.localRotation = Quaternion.Euler(flag ? 35f : (-35f), 0f, 0f);
				_shownCoupled = flag;
			}
		}

		public float GetHoverOffset()
		{
			return 0f;
		}

		public string GetHoverName()
		{
			if (!Front)
			{
				return "Rear coupling lever";
			}
			return "Front coupling lever";
		}

		public string GetHoverText()
		{
			string text = ((!Object.op_Implicit((Object)(object)_cart) || !_cart.IsReady) ? "\nCoupling unavailable" : (_cart.HasCouplingAt(Front) ? "\n[<color=yellow>$KEY_Use</color>] Uncouple this end\nStop the train before releasing." : "\n[<color=yellow>$KEY_Use</color>] Couple this end\nStop and align an open coupler nearby."));
			return Localization.instance.Localize(GetHoverName() + text);
		}

		public bool Interact(Humanoid user, bool hold, bool alt)
		{
			if (!hold && Object.op_Implicit((Object)(object)_cart))
			{
				Player val = (Player)(object)((user is Player) ? user : null);
				if (val != null && !((