Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ValheimVehiclesStabilizer v1.1.0
VVStabilizer.dll
Decompiled 2 hours agousing 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; } } } }