Decompiled source of ValheimVehiclesStabilizer v1.1.0

VVStabilizer.dll

Decompiled 2 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using VVStabilizer.Patches;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("VVStabilizer")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Freezes unattended ValheimRAFT vehicles and smooths control-handoff to stop the load-drift and takeover-jolt bugs.")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+9e4758b82ea2b91637b737b6793f2466ef6f1c9a")]
[assembly: AssemblyProduct("ValheimVehicles Stabilizer")]
[assembly: AssemblyTitle("VVStabilizer")]
[assembly: AssemblyVersion("0.1.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 VVStabilizer
{
	internal static class Config
	{
		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> FreezeUndriven;

		internal static ConfigEntry<float> SettleSeconds;

		internal static ConfigEntry<float> ReseatInterval;

		internal static ConfigEntry<float> MisseatedEpsilon;

		internal static ConfigEntry<bool> ReseatOnLogin;

		internal static ConfigEntry<bool> DebugLogging;

		internal static void Init(ConfigFile cfg)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Expected O, but got Unknown
			Enabled = cfg.Bind<bool>("General", "Enabled", true, "Master switch. If false, the plugin patches nothing and the game runs vanilla-ValheimRAFT.");
			FreezeUndriven = cfg.Bind<bool>("Stabilizer", "FreezeUndriven", true, "When the vehicle has no controlling driver, hold it kinematic and re-seated at the water/ground line so it cannot drift (fixes the fly-away). Runs only on the ZDO owner (the dedicated server for idle boats).");
			SettleSeconds = cfg.Bind<float>("Stabilizer", "SettleSeconds", 0.4f, new ConfigDescription("After a player takes the wheel, keep the vehicle kinematic + re-seated for this many seconds before releasing to normal dynamics (gentle wake-up; fixes the takeover jolt).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
			ReseatInterval = cfg.Bind<float>("Stabilizer", "ReseatInterval", 5f, new ConfigDescription("While undriven, re-run the (throttled) re-seat at most this often when the boat looks mis-seated. The cheap per-frame kinematic hold is unaffected.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 60f), Array.Empty<object>()));
			MisseatedEpsilon = cfg.Bind<float>("Stabilizer", "MisseatedHeightEpsilon", 0.5f, "How far (metres) the vehicle Y may sit above max(waterLevel, groundHeight) before the undriven loop considers it mis-seated and triggers a throttled re-seat.");
			ReseatOnLogin = cfg.Bind<bool>("Stabilizer", "ReseatOnLogin", true, "Re-seat the vehicle when a player logs in / spawns onto it (DynamicLocations), so players land on a seated, upright boat. Client-side.");
			DebugLogging = cfg.Bind<bool>("General", "DebugLogging", false, "Verbose logging of freeze/settle/reseat decisions. Leave off for production.");
		}
	}
	internal sealed class CtrlState
	{
		internal bool WasDriven;

		internal bool EverReseated;

		internal float SettleUntilTime;

		internal float LastReseatTime;
	}
	internal static class State
	{
		private static readonly ConditionalWeakTable<object, CtrlState> _table = new ConditionalWeakTable<object, CtrlState>();

		internal static CtrlState Get(object controller)
		{
			if (controller == null)
			{
				return null;
			}
			return _table.GetValue(controller, (object _) => new CtrlState());
		}
	}
	internal static class Log
	{
		private static ManualLogSource _log;

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

		internal static void Init(ManualLogSource src)
		{
			_log = src;
		}

		internal static void Info(string msg)
		{
			ManualLogSource log = _log;
			if (log != null)
			{
				log.LogInfo((object)msg);
			}
		}

		internal static void Warn(string msg)
		{
			ManualLogSource log = _log;
			if (log != null)
			{
				log.LogWarning((object)msg);
			}
		}

		internal static void Error(string msg)
		{
			ManualLogSource log = _log;
			if (log != null)
			{
				log.LogError((object)msg);
			}
		}

		internal static void Debug(string msg)
		{
			if (Config.DebugLogging != null && Config.DebugLogging.Value)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogInfo((object)("[dbg] " + msg));
				}
			}
		}

		internal static void ErrorOnce(string key, Exception e)
		{
			if (_seen.Add(key))
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogError((object)$"[{key}] {e}");
				}
			}
		}
	}
	[BepInPlugin("com.bigelowliam.vvstabilizer", "ValheimVehicles Stabilizer", "0.1.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		internal const string Guid = "com.bigelowliam.vvstabilizer";

		internal const string Ver = "0.1.1";

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Expected O, but got Unknown
			Log.Init(((BaseUnityPlugin)this).Logger);
			Config.Init(((BaseUnityPlugin)this).Config);
			if (!Config.Enabled.Value)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"VVStabilizer disabled via config; no patches applied.");
				return;
			}
			try
			{
				VV.Init();
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("VVStabilizer API bind failed; running inert: " + ex));
				return;
			}
			if (!VV.Ready)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"VVStabilizer: required ValheimVehicles API not found (version drift?). Plugin is inert — game runs normally.");
				return;
			}
			_harmony = new Harmony("com.bigelowliam.vvstabilizer");
			int num = 0;
			num += TryPatch(VV.GuardedFixedUpdate, typeof(UndrivenFreezePatch), "Postfix", "GuardedFixedUpdate (undriven freeze)");
			num += TryPatch(VV.OnControlsHandOff, typeof(HandoffSettlePatch), "Postfix", "OnControlsHandOff (takeover settle)");
			if (Config.ReseatOnLogin.Value)
			{
				num += TryPatch(VV.OnLoginMoveToZDO, typeof(LoginReseatPatch), "Postfix", "OnLoginMoveToZDO (login reseat)");
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("VVStabilizer {0} active — {1} patch(es) applied.", "0.1.1", num));
		}

		private int TryPatch(MethodBase target, Type patchType, string postfixName, string label)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			try
			{
				if (target == null)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("VVStabilizer: patch target not found, skipping: " + label));
					return 0;
				}
				MethodInfo method = patchType.GetMethod(postfixName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (method == null)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)("VVStabilizer: postfix method missing for " + label));
					return 0;
				}
				_harmony.Patch(target, (HarmonyMethod)null, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				((BaseUnityPlugin)this).Logger.LogInfo((object)("VVStabilizer: patched " + label));
				return 1;
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"VVStabilizer: failed to patch {label}: {arg}");
				return 0;
			}
		}

		private void OnDestroy()
		{
			try
			{
				Harmony harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
			}
			catch
			{
			}
		}
	}
	internal sealed class Member
	{
		private readonly FieldInfo _field;

		private readonly MethodInfo _getter;

		private readonly MethodInfo _setter;

		internal readonly string Name;

		internal bool Ok
		{
			get
			{
				if (!(_field != null))
				{
					return _getter != null;
				}
				return true;
			}
		}

		private Member(string name, FieldInfo f, MethodInfo g, MethodInfo s)
		{
			Name = name;
			_field = f;
			_getter = g;
			_setter = s;
		}

		internal static Member Resolve(Type type, params string[] names)
		{
			if (type != null && names != null)
			{
				foreach (string text in names)
				{
					PropertyInfo propertyInfo = AccessTools.Property(type, text);
					if (propertyInfo != null && propertyInfo.GetMethod != null)
					{
						return new Member(text, null, propertyInfo.GetMethod, propertyInfo.SetMethod);
					}
					FieldInfo fieldInfo = AccessTools.Field(type, text);
					if (fieldInfo != null)
					{
						return new Member(text, fieldInfo, null, null);
					}
				}
			}
			return new Member((names != null && names.Length != 0) ? names[0] : "?", null, null, null);
		}

		internal object Get(object instance)
		{
			try
			{
				if (_field != null)
				{
					return _field.GetValue(_field.IsStatic ? null : instance);
				}
				if (_getter != null)
				{
					return _getter.Invoke(_getter.IsStatic ? null : instance, null);
				}
			}
			catch (Exception e)
			{
				Log.ErrorOnce("get:" + Name, e);
			}
			return null;
		}

		internal void Set(object instance, object value)
		{
			try
			{
				if (_field != null)
				{
					_field.SetValue(_field.IsStatic ? null : instance, value);
				}
				else if (_setter != null)
				{
					_setter.Invoke(_setter.IsStatic ? null : instance, new object[1] { value });
				}
			}
			catch (Exception e)
			{
				Log.ErrorOnce("set:" + Name, e);
			}
		}
	}
	internal static class Reflect
	{
		internal static Type Type(string fullName)
		{
			return AccessTools.TypeByName(fullName);
		}

		internal static MethodInfo Method(Type type, string name, Type[] args = null)
		{
			if (!(type == null))
			{
				return AccessTools.Method(type, name, args, (Type[])null);
			}
			return null;
		}

		internal static bool AsBool(object o, bool fallback = false)
		{
			if (o is bool)
			{
				return (bool)o;
			}
			return fallback;
		}

		internal static int AsInt(object o, int fallback = 0)
		{
			if (o is int)
			{
				return (int)o;
			}
			return fallback;
		}
	}
	internal static class U
	{
		private static PropertyInfo _velProp;

		private static bool _velResolved;

		private static PropertyInfo VelocityProp(object rb)
		{
			if (_velResolved)
			{
				return _velProp;
			}
			_velResolved = true;
			Type type = rb.GetType();
			_velProp = type.GetProperty("velocity") ?? type.GetProperty("linearVelocity");
			return _velProp;
		}

		internal static void ZeroVelocity(Rigidbody rb)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			if (rb == null || rb.isKinematic)
			{
				return;
			}
			try
			{
				rb.angularVelocity = Vector3.zero;
			}
			catch
			{
			}
			try
			{
				PropertyInfo propertyInfo = VelocityProp(rb);
				if (propertyInfo != null && propertyInfo.CanWrite)
				{
					propertyInfo.SetValue(rb, Vector3.zero, null);
				}
			}
			catch
			{
			}
		}
	}
	internal static class VV
	{
		private const float HeightEps = 0.2f;

		private const float DefaultWaterLevel = 30f;

		internal static Type TVMC;

		internal static Type TManager;

		internal static Type TZSync;

		internal static Type TCommands;

		internal static Type TLogin;

		internal static Type TPlayer;

		internal static Type TZoneSystem;

		internal static Type TZNetScene;

		internal static Type TZDO;

		private static MethodInfo _haveControllingPlayer;

		private static MethodInfo _haveValidUser;

		private static MethodInfo _isOwner;

		private static MethodInfo _isInvalid;

		private static Member _manager;

		private static Member _body;

		private static Member _zsync;

		private static Member _hauling;

		private static Member _persistentId;

		private static Member _isLandVehicle;

		private static Member _movementController;

		private static MethodInfo _setKinematic;

		private static int _setKinematicArgc;

		private static MethodInfo _fixVehiclePosition;

		private static FieldInfo _localPlayer;

		private static Member _zoneInstance;

		private static Member _waterLevel;

		private static Member _znsInstance;

		private static MethodInfo _findInstance;

		private static MethodInfo _goGetComponent;

		private static MethodInfo _goGetComponentInParent;

		private static object _allowFlightEntry;

		private static object _waterBallastEntry;

		private static PropertyInfo _entryValueProp;

		internal static MethodInfo GuardedFixedUpdate { get; private set; }

		internal static MethodInfo OnControlsHandOff { get; private set; }

		internal static MethodInfo OnLoginMoveToZDO { get; private set; }

		internal static bool Ready { get; private set; }

		internal static bool CanReseat
		{
			get
			{
				if (_fixVehiclePosition != null)
				{
					return _persistentId.Ok;
				}
				return false;
			}
		}

		internal static void Init()
		{
			TVMC = Reflect.Type("ValheimVehicles.Controllers.VehicleMovementController");
			TManager = Reflect.Type("ValheimVehicles.Components.VehicleManager");
			TZSync = Reflect.Type("ValheimVehicles.Controllers.VehicleZSyncTransform");
			TCommands = Reflect.Type("ValheimVehicles.ConsoleCommands.VehicleCommands");
			TLogin = Reflect.Type("ValheimVehicles.ModSupport.DynamicLocationsLoginIntegration");
			TPlayer = Reflect.Type("Player");
			TZoneSystem = Reflect.Type("ZoneSystem");
			TZNetScene = Reflect.Type("ZNetScene");
			TZDO = Reflect.Type("ZDO");
			GuardedFixedUpdate = Reflect.Method(TVMC, "GuardedFixedUpdate", new Type[1] { typeof(float) });
			OnControlsHandOff = ((TPlayer != null) ? Reflect.Method(TVMC, "OnControlsHandOff", new Type[2] { TPlayer, TPlayer }) : null);
			OnLoginMoveToZDO = ((TLogin != null) ? AccessTools.Method(TLogin, "OnLoginMoveToZDO", (Type[])null, (Type[])null) : null);
			_haveControllingPlayer = Reflect.Method(TVMC, "HaveControllingPlayer");
			_haveValidUser = Reflect.Method(TVMC, "HaveValidUser");
			_isOwner = Reflect.Method(TVMC, "IsOwner");
			_isInvalid = Reflect.Method(TVMC, "IsInvalid");
			_manager = Member.Resolve(TVMC, "Manager");
			_body = Member.Resolve(TVMC, "m_body", "rigidbody");
			_zsync = Member.Resolve(TVMC, "zsyncTransform");
			_hauling = Member.Resolve(TVMC, "isPlayerHaulingVehicle");
			_persistentId = Member.Resolve(TManager, "PersistentZdoId");
			_isLandVehicle = Member.Resolve(TManager, "IsLandVehicle");
			_movementController = Member.Resolve(TManager, "MovementController");
			_setKinematic = Reflect.Method(TZSync, "SetKinematic", new Type[2]
			{
				typeof(bool),
				typeof(bool)
			});
			if (_setKinematic == null)
			{
				_setKinematic = Reflect.Method(TZSync, "SetKinematic", new Type[1] { typeof(bool) });
			}
			MethodInfo setKinematic = _setKinematic;
			_setKinematicArgc = (((object)setKinematic != null) ? setKinematic.GetParameters().Length : 0);
			_fixVehiclePosition = Reflect.Method(TCommands, "FixVehiclePosition", new Type[3]
			{
				typeof(int),
				typeof(float?),
				typeof(float?)
			});
			_localPlayer = ((TPlayer != null) ? AccessTools.Field(TPlayer, "m_localPlayer") : null);
			_zoneInstance = Member.Resolve(TZoneSystem, "instance", "m_instance");
			_waterLevel = Member.Resolve(TZoneSystem, "m_waterLevel");
			_znsInstance = Member.Resolve(TZNetScene, "instance", "m_instance");
			_findInstance = ((TZDO != null) ? Reflect.Method(TZNetScene, "FindInstance", new Type[1] { TZDO }) : null);
			_allowFlightEntry = StaticValue("ValheimVehicles.BepInExConfig.PropulsionConfig", "AllowFlight");
			_waterBallastEntry = StaticValue("ValheimVehicles.BepInExConfig.WaterConfig", "WaterBallastEnabled");
			Ready = TVMC != null && GuardedFixedUpdate != null && _isOwner != null && _haveControllingPlayer != null && _body.Ok;
			Log.Info($"API bind: VMC={TVMC != null} GFU={GuardedFixedUpdate != null} " + $"HandOff={OnControlsHandOff != null} Login={OnLoginMoveToZDO != null} " + $"body={_body.Ok} zsync={_zsync.Ok} setKinematic={_setKinematic != null} " + $"isOwner={_isOwner != null} isInvalid={_isInvalid != null} " + $"haveDriver={_haveControllingPlayer != null} manager={_manager.Ok} " + $"persistentId={_persistentId.Ok} isLand={_isLandVehicle.Ok} " + $"fixPos={_fixVehiclePosition != null} localPlayer={_localPlayer != null} " + $"water={_zoneInstance.Ok && _waterLevel.Ok} flightCfg={_allowFlightEntry != null} " + $"ballastCfg={_waterBallastEntry != null}");
		}

		internal static bool IsOwner(object vmc)
		{
			if (_isOwner != null)
			{
				return Reflect.AsBool(SafeInvoke(_isOwner, vmc, null));
			}
			return false;
		}

		internal static bool HaveControllingPlayer(object vmc)
		{
			if (_haveControllingPlayer != null)
			{
				return Reflect.AsBool(SafeInvoke(_haveControllingPlayer, vmc, null));
			}
			return false;
		}

		internal static bool HaveValidUser(object vmc)
		{
			if (_haveValidUser != null)
			{
				return Reflect.AsBool(SafeInvoke(_haveValidUser, vmc, null));
			}
			return false;
		}

		internal static bool IsDriven(object vmc)
		{
			if (!HaveValidUser(vmc))
			{
				return HaveControllingPlayer(vmc);
			}
			return true;
		}

		internal static bool IsInvalid(object vmc)
		{
			if (_isInvalid != null)
			{
				return Reflect.AsBool(SafeInvoke(_isInvalid, vmc, null));
			}
			return false;
		}

		internal static bool IsHauling(object vmc)
		{
			if (_hauling.Ok)
			{
				return Reflect.AsBool(_hauling.Get(vmc));
			}
			return false;
		}

		internal static object Manager(object vmc)
		{
			if (!_manager.Ok)
			{
				return null;
			}
			return _manager.Get(vmc);
		}

		internal static bool IsLandVehicle(object vmc)
		{
			object obj = Manager(vmc);
			if (obj != null && _isLandVehicle.Ok)
			{
				return Reflect.AsBool(_isLandVehicle.Get(obj));
			}
			return false;
		}

		internal static int PersistentId(object vmc)
		{
			object obj = Manager(vmc);
			if (obj == null || !_persistentId.Ok)
			{
				return 0;
			}
			return Reflect.AsInt(_persistentId.Get(obj));
		}

		internal static Rigidbody Body(object vmc)
		{
			object obj = _body.Get(vmc);
			return (Rigidbody)((obj is Rigidbody) ? obj : null);
		}

		internal static object LocalPlayer()
		{
			try
			{
				return _localPlayer?.GetValue(null);
			}
			catch
			{
				return null;
			}
		}

		private static bool ReadEntryBool(object entry)
		{
			if (entry == null)
			{
				return false;
			}
			try
			{
				if (_entryValueProp == null || _entryValueProp.DeclaringType != entry.GetType())
				{
					_entryValueProp = entry.GetType().GetProperty("Value");
				}
				object obj = _entryValueProp?.GetValue(entry);
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch
			{
				return false;
			}
		}

		internal static bool FlightOrBallastOn()
		{
			if (!ReadEntryBool(_allowFlightEntry))
			{
				return ReadEntryBool(_waterBallastEntry);
			}
			return true;
		}

		internal static void FreezeBody(object vmc)
		{
			Rigidbody val = Body(vmc);
			if (val != null)
			{
				U.ZeroVelocity(val);
				try
				{
					val.isKinematic = true;
				}
				catch
				{
				}
			}
			if (!(_setKinematic != null) || !_zsync.Ok)
			{
				return;
			}
			object obj2 = _zsync.Get(vmc);
			if (obj2 != null)
			{
				try
				{
					SafeInvoke(_setKinematic, obj2, (_setKinematicArgc != 2) ? new object[1] { true } : new object[2] { true, true });
				}
				catch
				{
				}
			}
		}

		internal static float WaterLevel()
		{
			try
			{
				if (_zoneInstance.Ok && _waterLevel.Ok)
				{
					object obj = _zoneInstance.Get(null);
					if (obj != null)
					{
						object obj2 = _waterLevel.Get(obj);
						if (obj2 is float)
						{
							return (float)obj2;
						}
					}
				}
			}
			catch
			{
			}
			return 30f;
		}

		internal static void Reseat(object vmc, bool zeroVelocity)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				int num = PersistentId(vmc);
				if (num != 0 && _fixVehiclePosition != null)
				{
					float num2 = WaterLevel();
					object obj = num2 - 10000f;
					object obj2 = num2 + 0.2f;
					SafeInvoke(_fixVehiclePosition, null, new object[3] { num, obj, obj2 });
				}
				Rigidbody val = Body(vmc);
				if (val != null)
				{
					try
					{
						Quaternion rotation = val.rotation;
						float y = ((Quaternion)(ref rotation)).eulerAngles.y;
						val.rotation = Quaternion.Euler(0f, y, 0f);
					}
					catch
					{
					}
					if (zeroVelocity)
					{
						U.ZeroVelocity(val);
					}
				}
				try
				{
					Physics.SyncTransforms();
				}
				catch
				{
				}
			}
			catch (Exception e)
			{
				Log.ErrorOnce("Reseat", e);
			}
		}

		internal static IEnumerator WrapLoginReseat(IEnumerator inner, object zdo)
		{
			if (inner != null)
			{
				while (true)
				{
					object obj = null;
					bool flag;
					try
					{
						flag = inner.MoveNext();
						if (flag)
						{
							obj = inner.Current;
						}
					}
					catch
					{
						flag = false;
					}
					if (!flag)
					{
						break;
					}
					yield return obj;
				}
			}
			object obj3 = null;
			try
			{
				obj3 = ResolveControllerFromZdo(zdo);
			}
			catch (Exception e)
			{
				Log.ErrorOnce("LoginResolve", e);
			}
			if (obj3 == null)
			{
				yield break;
			}
			try
			{
				if (!IsLandVehicle(obj3) && !FlightOrBallastOn())
				{
					Reseat(obj3, zeroVelocity: true);
				}
			}
			catch (Exception e2)
			{
				Log.ErrorOnce("LoginReseat", e2);
			}
		}

		private static object ResolveControllerFromZdo(object zdo)
		{
			if (zdo == null || _findInstance == null || !_znsInstance.Ok)
			{
				return null;
			}
			object obj = _znsInstance.Get(null);
			if (obj == null)
			{
				return null;
			}
			object obj2 = SafeInvoke(_findInstance, obj, new object[1] { zdo });
			if (obj2 == null)
			{
				return null;
			}
			object componentOf = GetComponentOf(obj2, TManager);
			if (componentOf == null)
			{
				return null;
			}
			if (!_movementController.Ok)
			{
				return null;
			}
			return _movementController.Get(componentOf);
		}

		private static object GetComponentOf(object go, Type compType)
		{
			if (go == null || compType == null)
			{
				return null;
			}
			try
			{
				if (_goGetComponent == null)
				{
					_goGetComponent = go.GetType().GetMethod("GetComponent", new Type[1] { typeof(Type) });
				}
				object obj = _goGetComponent?.Invoke(go, new object[1] { compType });
				if (obj != null && !obj.Equals(null))
				{
					return obj;
				}
				if (_goGetComponentInParent == null)
				{
					_goGetComponentInParent = go.GetType().GetMethod("GetComponentInParent", new Type[1] { typeof(Type) });
				}
				object obj2 = _goGetComponentInParent?.Invoke(go, new object[1] { compType });
				if (obj2 != null && !obj2.Equals(null))
				{
					return obj2;
				}
			}
			catch
			{
			}
			return null;
		}

		private static object SafeInvoke(MethodInfo mi, object instance, object[] args)
		{
			try
			{
				return mi.Invoke(instance, args);
			}
			catch (TargetInvocationException ex)
			{
				Log.ErrorOnce(mi.Name, ex.InnerException ?? ex);
				return null;
			}
			catch (Exception e)
			{
				Log.ErrorOnce(mi.Name, e);
				return null;
			}
		}

		private static object StaticValue(string typeName, string memberName)
		{
			try
			{
				Type type = Reflect.Type(typeName);
				if (type == null)
				{
					return null;
				}
				Member member = Member.Resolve(type, memberName);
				return member.Ok ? member.Get(null) : null;
			}
			catch
			{
				return null;
			}
		}
	}
}
namespace VVStabilizer.Patches
{
	internal static class HandoffSettlePatch
	{
		internal static void Postfix(object __instance, object __0)
		{
			if (__instance == null || !Config.Enabled.Value || !VV.Ready)
			{
				return;
			}
			try
			{
				object obj = VV.LocalPlayer();
				if (obj != null && __0 == obj && !VV.IsLandVehicle(__instance) && !VV.FlightOrBallastOn())
				{
					if (VV.CanReseat)
					{
						VV.Reseat(__instance, zeroVelocity: true);
					}
					VV.FreezeBody(__instance);
					CtrlState ctrlState = State.Get(__instance);
					float num = Config.SettleSeconds.Value;
					if (num < 0f)
					{
						num = 0f;
					}
					if (num > 2f)
					{
						num = 2f;
					}
					ctrlState.SettleUntilTime = Time.time + num;
					ctrlState.WasDriven = true;
					Log.Debug($"handoff settle {num}s");
				}
			}
			catch (Exception e)
			{
				Log.ErrorOnce("HandoffSettle", e);
			}
		}
	}
	internal static class LoginReseatPatch
	{
		internal static void Postfix(ref IEnumerator __result, object __0)
		{
			if (!Config.Enabled.Value || !Config.ReseatOnLogin.Value || !VV.Ready || !VV.CanReseat || __result == null)
			{
				return;
			}
			try
			{
				__result = VV.WrapLoginReseat(__result, __0);
			}
			catch (Exception e)
			{
				Log.ErrorOnce("LoginReseatPatch", e);
			}
		}
	}
	internal static class UndrivenFreezePatch
	{
		internal static void Postfix(object __instance)
		{
			if (__instance == null || !Config.Enabled.Value || !Config.FreezeUndriven.Value || !VV.Ready)
			{
				return;
			}
			try
			{
				if (VV.IsInvalid(__instance) || !VV.IsOwner(__instance) || VV.IsLandVehicle(__instance) || VV.IsHauling(__instance) || VV.FlightOrBallastOn())
				{
					return;
				}
				CtrlState ctrlState = State.Get(__instance);
				float time = Time.time;
				if (time < ctrlState.SettleUntilTime)
				{
					VV.FreezeBody(__instance);
					return;
				}
				if (VV.IsDriven(__instance))
				{
					ctrlState.WasDriven = true;
					return;
				}
				bool wasDriven = ctrlState.WasDriven;
				bool flag = !ctrlState.EverReseated;
				bool flag2 = time - ctrlState.LastReseatTime > Config.ReseatInterval.Value && IsMisseated(__instance);
				if (VV.CanReseat && (wasDriven || flag || flag2))
				{
					VV.Reseat(__instance, zeroVelocity: true);
					ctrlState.LastReseatTime = time;
					ctrlState.EverReseated = true;
					Log.Debug($"reseat undriven (transition={wasDriven} first={flag} periodic={flag2})");
				}
				VV.FreezeBody(__instance);
				ctrlState.WasDriven = false;
			}
			catch (Exception e)
			{
				Log.ErrorOnce("UndrivenFreeze", e);
			}
		}

		private static bool IsMisseated(object vmc)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			Rigidbody val = VV.Body(vmc);
			if (val == null)
			{
				return false;
			}
			try
			{
				float y = val.position.y;
				float num = VV.WaterLevel();
				return y > num + Config.MisseatedEpsilon.Value;
			}
			catch
			{
				return false;
			}
		}
	}
}