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 ValheimRaftBedFix v0.1.5
BepInEx/plugins/ValheimRaftBedFix/ValheimRaftBedFix.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; 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; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ValheimRaftBedFix")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.5.0")] [assembly: AssemblyInformationalVersion("0.1.5")] [assembly: AssemblyProduct("ValheimRaftBedFix")] [assembly: AssemblyTitle("ValheimRaftBedFix")] [assembly: AssemblyVersion("0.1.5.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 ValheimRaftBedFix { public static class MobileShipDetector { public const string ShipGuidZdoKey = "ValheimRaftBedFix_ShipGuid"; private const string ShipRegistryPositionZdoKey = "ValheimRaftBedFix_ShipRegistryPosition"; private const string ShipRegistryZoneZdoKey = "ValheimRaftBedFix_ShipRegistryZone"; private const string ShipRegistryUpdatedZdoKey = "ValheimRaftBedFix_ShipRegistryUpdated"; private const float NearbySearchRadius = 8f; private const int MinimumVehicleScore = 100; public static bool TryFindVehicleRoot(Player player, out Transform vehicleRoot, out ZNetView vehicleView) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) vehicleRoot = null; vehicleView = null; if ((Object)(object)player == (Object)null) { return false; } Ship componentInParent = ((Component)player).GetComponentInParent<Ship>(); if ((Object)(object)componentInParent != (Object)null && TryGetValidView(((Component)componentInParent).transform, out vehicleView)) { vehicleRoot = ((Component)componentInParent).transform; RegisterShip(vehicleView, vehicleRoot); return true; } if (TryFindBestVehicleRootFromTransform(((Component)player).transform, ((Component)player).transform.position, out vehicleRoot, out vehicleView)) { RegisterShip(vehicleView, vehicleRoot); return true; } Collider[] array = Physics.OverlapSphere(((Component)player).transform.position, 8f); foreach (Collider val in array) { if (!((Object)(object)val == (Object)null) && TryFindBestVehicleRootFromTransform(((Component)val).transform, ((Component)player).transform.position, out var vehicleRoot2, out var vehicleView2)) { vehicleRoot = vehicleRoot2; vehicleView = vehicleView2; RegisterShip(vehicleView, vehicleRoot); return true; } } return false; } public static bool IsShipStable(Transform vehicleRoot) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)vehicleRoot == (Object)null) { return false; } Rigidbody val = ((Component)vehicleRoot).GetComponent<Rigidbody>() ?? ((Component)vehicleRoot).GetComponentInParent<Rigidbody>(); if ((Object)(object)val != (Object)null && Mathf.Abs(val.linearVelocity.y) > 0.15f) { return false; } return true; } public static string GetOrCreateShipGuid(ZNetView vehicleView) { if ((Object)(object)vehicleView == (Object)null || !vehicleView.IsValid()) { return null; } if (LooksLikeHelperTransform(((Component)vehicleView).transform) && !HasDurableVehicleMarker(((Component)vehicleView).transform, includeChildren: true)) { ValheimRaftBedFixPlugin.DebugLog("Refusing to create ship GUID on helper/transient vehicle object."); return null; } ZDO zDO = vehicleView.GetZDO(); if (zDO == null) { return null; } string text = zDO.GetString("ValheimRaftBedFix_ShipGuid", ""); if (string.IsNullOrWhiteSpace(text)) { text = Guid.NewGuid().ToString("N"); zDO.Set("ValheimRaftBedFix_ShipGuid", text); ValheimRaftBedFixPlugin.DebugLog("Generated ship GUID " + text); } return text; } public static bool TryResolveShipByGuid(string shipGuid, out Transform vehicleRoot, out ZNetView vehicleView) { vehicleRoot = null; vehicleView = null; if (string.IsNullOrWhiteSpace(shipGuid)) { return false; } ZNetView[] array = Object.FindObjectsByType<ZNetView>((FindObjectsSortMode)0); foreach (ZNetView val in array) { if ((Object)(object)val == (Object)null || !val.IsValid()) { continue; } ZDO zDO = val.GetZDO(); if (zDO == null || !string.Equals(zDO.GetString("ValheimRaftBedFix_ShipGuid", ""), shipGuid, StringComparison.OrdinalIgnoreCase)) { continue; } if (!TryNormalizeVehicleRootFromView(val, out var vehicleRoot2, out var vehicleView2)) { ValheimRaftBedFixPlugin.DebugLog("Found GUID " + shipGuid + ", but object is not a durable ship root yet."); continue; } if ((Object)(object)vehicleView2 != (Object)(object)val && (Object)(object)vehicleView2 != (Object)null && vehicleView2.IsValid()) { ZDO zDO2 = vehicleView2.GetZDO(); if (zDO2 != null && string.IsNullOrWhiteSpace(zDO2.GetString("ValheimRaftBedFix_ShipGuid", ""))) { zDO2.Set("ValheimRaftBedFix_ShipGuid", shipGuid); } } vehicleRoot = vehicleRoot2; vehicleView = vehicleView2; RegisterShip(vehicleView, vehicleRoot); return true; } return false; } public static void RegisterShip(ZNetView vehicleView, Transform vehicleRoot) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0053: 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_0071: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)vehicleView == (Object)null || !vehicleView.IsValid()) { return; } ZDO zDO = vehicleView.GetZDO(); if (zDO != null) { string @string = zDO.GetString("ValheimRaftBedFix_ShipGuid", ""); if (!string.IsNullOrWhiteSpace(@string)) { Vector3 val = (((Object)(object)vehicleRoot != (Object)null) ? vehicleRoot.position : ((Component)vehicleView).transform.position); Vector2i zone = GetZone(val); long num = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); zDO.Set("ValheimRaftBedFix_ShipRegistryPosition", FormatVector3(val)); zDO.Set("ValheimRaftBedFix_ShipRegistryZone", FormatZone(zone)); zDO.Set("ValheimRaftBedFix_ShipRegistryUpdated", num.ToString(CultureInfo.InvariantCulture)); MobileShipRegistry.RegisterObservedShip(@string, val, zone); } } } public static bool TryGetRegisteredShipPosition(string shipGuid, out Vector3 position, out Vector2i zone) { //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_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) position = Vector3.zero; zone = new Vector2i(0, 0); if (string.IsNullOrWhiteSpace(shipGuid)) { return false; } if (MobileShipRegistry.TryGetRecord(shipGuid, out var record)) { position = record.Position; zone = record.Zone; return true; } ZNetView[] array = Object.FindObjectsByType<ZNetView>((FindObjectsSortMode)0); foreach (ZNetView val in array) { if ((Object)(object)val == (Object)null || !val.IsValid()) { continue; } ZDO zDO = val.GetZDO(); if (zDO != null && string.Equals(zDO.GetString("ValheimRaftBedFix_ShipGuid", ""), shipGuid, StringComparison.OrdinalIgnoreCase)) { string @string = zDO.GetString("ValheimRaftBedFix_ShipRegistryPosition", ""); string string2 = zDO.GetString("ValheimRaftBedFix_ShipRegistryZone", ""); if (!TryParseVector3(@string, out position)) { position = ((Component)val).transform.position; } if (!TryParseZone(string2, out zone)) { zone = GetZone(position); } return true; } } return false; } public static void ScanLoadedShipsForRegistry() { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } ZNetView[] array = Object.FindObjectsByType<ZNetView>((FindObjectsSortMode)0); foreach (ZNetView val in array) { if ((Object)(object)val == (Object)null || !val.IsValid()) { continue; } ZDO zDO = val.GetZDO(); if (zDO == null || !TryNormalizeVehicleRootFromView(val, out var vehicleRoot, out var vehicleView) || (Object)(object)vehicleView == (Object)null || !vehicleView.IsValid()) { continue; } ZDO zDO2 = vehicleView.GetZDO(); if (zDO2 != null) { string @string = zDO.GetString("ValheimRaftBedFix_ShipGuid", ""); if (!string.IsNullOrWhiteSpace(@string) && string.IsNullOrWhiteSpace(zDO2.GetString("ValheimRaftBedFix_ShipGuid", ""))) { zDO2.Set("ValheimRaftBedFix_ShipGuid", @string); } if (!string.IsNullOrWhiteSpace(GetOrCreateShipGuid(vehicleView))) { RegisterShip(vehicleView, vehicleRoot); } } } } private static bool TryFindBestVehicleRootFromTransform(Transform start, Vector3 referencePosition, out Transform vehicleRoot, out ZNetView vehicleView) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) vehicleRoot = null; vehicleView = null; if ((Object)(object)start == (Object)null) { return false; } int bestScore = int.MinValue; Transform bestRoot = null; ZNetView bestView = null; Transform val = start; while ((Object)(object)val != (Object)null) { ZNetView[] components = ((Component)val).GetComponents<ZNetView>(); for (int i = 0; i < components.Length; i++) { ConsiderView(components[i]); } components = ((Component)val).GetComponentsInParent<ZNetView>(true); for (int i = 0; i < components.Length; i++) { ConsiderView(components[i]); } if (HasVehicleHintMarker(val, includeChildren: false) || HasDurableVehicleMarker(val, includeChildren: false) || LooksLikeVehicleTransform(val)) { components = ((Component)val).GetComponentsInChildren<ZNetView>(true); for (int i = 0; i < components.Length; i++) { ConsiderView(components[i]); } } val = val.parent; } if ((Object)(object)bestRoot == (Object)null || (Object)(object)bestView == (Object)null || bestScore < 100) { return false; } vehicleRoot = bestRoot; vehicleView = bestView; return true; void ConsiderView(ZNetView view) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) if (TryBuildVehicleCandidate(view, referencePosition, out var candidateRoot, out var candidateView, out var score) && score > bestScore) { bestScore = score; bestRoot = candidateRoot; bestView = candidateView; } } } private static bool TryNormalizeVehicleRootFromView(ZNetView view, out Transform vehicleRoot, out ZNetView vehicleView) { //IL_001d: 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) vehicleRoot = null; vehicleView = null; if (!TryBuildVehicleCandidate(view, ((Object)(object)view != (Object)null) ? ((Component)view).transform.position : Vector3.zero, out var candidateRoot, out var candidateView, out var score)) { return false; } if (score < 100) { return false; } vehicleRoot = candidateRoot; vehicleView = candidateView; return true; } private static bool TryBuildVehicleCandidate(ZNetView view, Vector3 referencePosition, out Transform candidateRoot, out ZNetView candidateView, out int score) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) candidateRoot = null; candidateView = null; score = int.MinValue; if ((Object)(object)view == (Object)null || !view.IsValid()) { return false; } if (view.GetZDO() == null) { return false; } Transform val = FindBestDurableRoot(((Component)view).transform); int num = ScoreVehicleCandidate(val, view, referencePosition); if (num < 100) { return false; } ZNetView component = ((Component)val).GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null && component.IsValid() && component.GetZDO() != null) { candidateView = component; } else { candidateView = view; } candidateRoot = val; score = num; return true; } private static Transform FindBestDurableRoot(Transform start) { if ((Object)(object)start == (Object)null) { return null; } Transform result = start; int num = ScoreRootTransform(start); Transform parent = start.parent; while ((Object)(object)parent != (Object)null) { int num2 = ScoreRootTransform(parent); if (num2 > num) { result = parent; num = num2; } parent = parent.parent; } return result; } private static int ScoreVehicleCandidate(Transform root, ZNetView view, Vector3 referencePosition) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)root == (Object)null || (Object)(object)view == (Object)null) { return int.MinValue; } int num = ScoreRootTransform(root); if (TryGetValidView(root, out var view2) && (Object)(object)view2 == (Object)(object)view) { num += 75; } if ((Object)(object)((Component)root).GetComponent<Rigidbody>() != (Object)null) { num += 60; } else if ((Object)(object)((Component)root).GetComponentInParent<Rigidbody>() != (Object)null) { num += 25; } Vector3 val = root.position - referencePosition; if (((Vector3)(ref val)).sqrMagnitude <= 64f) { num += 40; } if (LooksLikeHelperTransform(((Component)view).transform) && (Object)(object)((Component)view).transform == (Object)(object)root) { num -= 500; } return num; } private static int ScoreRootTransform(Transform root) { if ((Object)(object)root == (Object)null) { return int.MinValue; } int num = 0; if ((Object)(object)((Component)root).GetComponent<Ship>() != (Object)null) { num += 1000; } if (HasDurableVehicleMarker(root, includeChildren: false)) { num += 500; } if (HasDurableVehicleMarker(root, includeChildren: true)) { num += 250; } if (HasWeakVehicleMarker(root, includeChildren: false)) { num += 100; } if (HasWeakVehicleMarker(root, includeChildren: true)) { num += 50; } if (HasOnlyHelperMarker(root)) { num -= 300; } if (LooksLikeVehicleTransform(root)) { num += 50; } if (LooksLikeHelperTransform(root)) { num -= 400; } return num; } private static bool TryGetValidView(Transform transform, out ZNetView view) { view = null; if ((Object)(object)transform == (Object)null) { return false; } view = ((Component)transform).GetComponent<ZNetView>() ?? ((Component)transform).GetComponentInParent<ZNetView>() ?? ((Component)transform).GetComponentInChildren<ZNetView>(); if ((Object)(object)view != (Object)null && view.IsValid()) { return view.GetZDO() != null; } return false; } private static bool HasDurableVehicleMarker(Transform transform, bool includeChildren) { return HasComponentName(transform, includeChildren, IsDurableVehicleComponentName); } private static bool HasWeakVehicleMarker(Transform transform, bool includeChildren) { return HasComponentName(transform, includeChildren, IsWeakVehicleComponentName); } private static bool HasVehicleHintMarker(Transform transform, bool includeChildren) { return HasComponentName(transform, includeChildren, IsVehicleHintComponentName); } private static bool HasOnlyHelperMarker(Transform transform) { if ((Object)(object)transform == (Object)null) { return false; } bool num = HasVehicleHintMarker(transform, includeChildren: false); bool flag = HasDurableVehicleMarker(transform, includeChildren: false); if (num) { return !flag; } return false; } private static bool HasComponentName(Transform transform, bool includeChildren, Func<string, bool> predicate) { if ((Object)(object)transform == (Object)null) { return false; } Component[] array = (includeChildren ? ((Component)transform).GetComponentsInChildren<Component>(true) : ((Component)transform).GetComponents<Component>()); foreach (Component val in array) { if (!((Object)(object)val == (Object)null)) { string text = ((object)val).GetType().FullName ?? ((object)val).GetType().Name ?? string.Empty; if (predicate(text.ToLowerInvariant())) { return true; } } } return false; } private static bool IsDurableVehicleComponentName(string typeName) { if (string.IsNullOrWhiteSpace(typeName)) { return false; } if (!typeName.Contains("vehiclemovementcontroller") && !typeName.Contains("vehiclepiecescontroller") && !typeName.Contains("watervehicle") && !typeName.Contains("moveablebaserootcomponent")) { return typeName.Contains("moveablebaseshipcomponent"); } return true; } private static bool IsWeakVehicleComponentName(string typeName) { if (string.IsNullOrWhiteSpace(typeName)) { return false; } if (!typeName.Contains("vehicleonboardcontroller") && !typeName.Contains("valheimraft")) { return typeName.Contains("valheimvehicles"); } return true; } private static bool IsVehicleHintComponentName(string typeName) { if (string.IsNullOrWhiteSpace(typeName)) { return false; } if (!IsDurableVehicleComponentName(typeName) && !IsWeakVehicleComponentName(typeName)) { return typeName.Contains("convexhullboundaryconstraint"); } return true; } private static bool LooksLikeVehicleTransform(Transform transform) { if ((Object)(object)transform == (Object)null) { return false; } string text = ((Object)transform).name.ToLowerInvariant(); if (!text.Contains("raft") && !text.Contains("ship") && !text.Contains("vehicle")) { return text.Contains("moveablebase"); } return true; } private static bool LooksLikeHelperTransform(Transform transform) { if ((Object)(object)transform == (Object)null) { return false; } string text = ((Object)transform).name.ToLowerInvariant(); if (!text.Contains("convexhull") && !text.Contains("convex_hull") && !text.Contains("boundary") && !text.Contains("constraint") && !text.Contains("collider") && !text.Contains("trigger") && !text.Contains("helper")) { return text.Contains("valheimvehicles_convexhull"); } return true; } private static Vector2i GetZone(Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return MobileZoneMath.GetZone(position); } private static string FormatVector3(Vector3 value) { return value.x.ToString(CultureInfo.InvariantCulture) + "," + value.y.ToString(CultureInfo.InvariantCulture) + "," + value.z.ToString(CultureInfo.InvariantCulture); } private static bool TryParseVector3(string raw, out Vector3 value) { //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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) value = Vector3.zero; if (string.IsNullOrWhiteSpace(raw)) { return false; } string[] array = raw.Split(','); if (array.Length != 3) { return false; } if (!float.TryParse(array[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return false; } if (!float.TryParse(array[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { return false; } if (!float.TryParse(array[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3)) { return false; } value = new Vector3(result, result2, result3); return true; } private static string FormatZone(Vector2i zone) { return $"{zone.x},{zone.y}"; } private static bool TryParseZone(string raw, out Vector2i zone) { zone = new Vector2i(0, 0); if (string.IsNullOrWhiteSpace(raw)) { return false; } string[] array = raw.Split(','); if (array.Length != 2) { return false; } if (!int.TryParse(array[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { return false; } if (!int.TryParse(array[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2)) { return false; } zone = new Vector2i(result, result2); return true; } } public static class MobileShipRegistry { public sealed class ShipRecord { public string ShipGuid; public Vector3 Position; public Vector2i Zone; public long UpdatedUnixSeconds; } private const string RpcRegisterShip = "ValheimRaftBedFix_RPC_RegisterShip"; private const string RpcRequestShip = "ValheimRaftBedFix_RPC_RequestShip"; private const string RpcShipRecord = "ValheimRaftBedFix_RPC_ShipRecord"; private const float ClientRegisterThrottleSeconds = 1f; private const float ClientRequestThrottleSeconds = 1f; private const float SaveThrottleSeconds = 10f; private static readonly Dictionary<string, ShipRecord> Records = new Dictionary<string, ShipRecord>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, float> NextClientRegisterTime = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, float> NextClientRequestTime = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); private static ZRoutedRpc _registeredRpcInstance; private static long _loadedWorldId = long.MinValue; private static bool _serverRegistryLoaded; private static bool _serverRegistryDirty; private static float _nextServerRegistrySaveTime; private static bool IsServer { get { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } private static long CurrentWorldId { get { if (!((Object)(object)ZNet.instance != (Object)null)) { return 0L; } return ZNet.instance.GetWorldUID(); } } private static string RegistryPath => Path.Combine(Paths.ConfigPath, $"ValheimRaftBedFix.ship-registry.server.{CurrentWorldId}.txt"); public static void Update() { RegisterRpcsIfNeeded(); ResetForWorldIfNeeded(); if (IsServer) { LoadServerRegistryIfNeeded(); if (_serverRegistryDirty && Time.realtimeSinceStartup >= _nextServerRegistrySaveTime) { SaveServerRegistry(); } } } public static void FlushServerRegistryIfDirty() { if (IsServer) { LoadServerRegistryIfNeeded(); SaveServerRegistry(); } } public static void RegisterObservedShip(string shipGuid, Vector3 position, Vector2i zone) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) if (!string.IsNullOrWhiteSpace(shipGuid)) { long updatedUnixSeconds = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); ShipRecord record = new ShipRecord { ShipGuid = shipGuid, Position = position, Zone = zone, UpdatedUnixSeconds = updatedUnixSeconds }; UpsertClientCache(record); if (IsServer) { LoadServerRegistryIfNeeded(); UpsertServerRecord(record, saveSoon: false); } else { SendObservedShipToServer(record); } } } public static bool TryGetRecord(string shipGuid, out ShipRecord record) { record = null; if (string.IsNullOrWhiteSpace(shipGuid)) { return false; } ResetForWorldIfNeeded(); if (IsServer) { LoadServerRegistryIfNeeded(); } if (!Records.TryGetValue(shipGuid, out var value)) { return false; } record = CloneRecord(value); return true; } public static void RequestShipRecord(string shipGuid) { if (string.IsNullOrWhiteSpace(shipGuid)) { return; } RegisterRpcsIfNeeded(); ResetForWorldIfNeeded(); if (IsServer) { LoadServerRegistryIfNeeded(); } else if (ZRoutedRpc.instance != null) { float realtimeSinceStartup = Time.realtimeSinceStartup; if (!NextClientRequestTime.TryGetValue(shipGuid, out var value) || !(realtimeSinceStartup < value)) { NextClientRequestTime[shipGuid] = realtimeSinceStartup + 1f; ZRoutedRpc.instance.InvokeRoutedRPC(GetServerPeerId(), "ValheimRaftBedFix_RPC_RequestShip", new object[1] { shipGuid }); } } } private static void SendObservedShipToServer(ShipRecord record) { //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) if (record == null || ZRoutedRpc.instance == null) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (NextClientRegisterTime.TryGetValue(record.ShipGuid, out var value) && realtimeSinceStartup < value && Records.TryGetValue(record.ShipGuid, out var value2)) { Vector3 val = value2.Position - record.Position; if (((Vector3)(ref val)).sqrMagnitude < 4f) { return; } } NextClientRegisterTime[record.ShipGuid] = realtimeSinceStartup + 1f; ZRoutedRpc.instance.InvokeRoutedRPC(GetServerPeerId(), "ValheimRaftBedFix_RPC_RegisterShip", new object[3] { record.ShipGuid ?? string.Empty, record.Position, FormatZone(record.Zone) }); } private static void RegisterRpcsIfNeeded() { if (ZRoutedRpc.instance != null && _registeredRpcInstance != ZRoutedRpc.instance) { _registeredRpcInstance = ZRoutedRpc.instance; ZRoutedRpc.instance.Register<string, Vector3, string>("ValheimRaftBedFix_RPC_RegisterShip", (Action<long, string, Vector3, string>)RPC_RegisterShip); ZRoutedRpc.instance.Register<string>("ValheimRaftBedFix_RPC_RequestShip", (Action<long, string>)RPC_RequestShip); ZRoutedRpc.instance.Register<string, bool, Vector3, string>("ValheimRaftBedFix_RPC_ShipRecord", (Method<string, bool, Vector3, string>)RPC_ShipRecord); } } private static void RPC_RegisterShip(long sender, string shipGuid, Vector3 position, string zoneRaw) { //IL_001d: 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) //IL_0035: Unknown result type (might be due to invalid IL or missing references) if (!IsServer) { return; } try { if (!string.IsNullOrWhiteSpace(shipGuid)) { Vector2i zone; Vector2i zone2 = (TryParseZone(zoneRaw, out zone) ? zone : GetZone(position)); ShipRecord record = new ShipRecord { ShipGuid = shipGuid, Position = position, Zone = zone2, UpdatedUnixSeconds = DateTimeOffset.UtcNow.ToUnixTimeSeconds() }; LoadServerRegistryIfNeeded(); UpsertServerRecord(record, saveSoon: false); } } catch (Exception ex) { ManualLogSource log = ValheimRaftBedFixPlugin.Log; if (log != null) { log.LogWarning((object)("Failed to handle ship registry update RPC: " + ex.Message)); } } } private static void RPC_RequestShip(long sender, string shipGuid) { //IL_008b: 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) if (!IsServer) { return; } try { if (!string.IsNullOrWhiteSpace(shipGuid)) { LoadServerRegistryIfNeeded(); if (Records.TryGetValue(shipGuid, out var value)) { ZRoutedRpc.instance.InvokeRoutedRPC(sender, "ValheimRaftBedFix_RPC_ShipRecord", new object[4] { shipGuid, true, value.Position, FormatZone(value.Zone) }); } else { ZRoutedRpc.instance.InvokeRoutedRPC(sender, "ValheimRaftBedFix_RPC_ShipRecord", new object[4] { shipGuid, false, Vector3.zero, string.Empty }); } } } catch (Exception ex) { ManualLogSource log = ValheimRaftBedFixPlugin.Log; if (log != null) { log.LogWarning((object)("Failed to handle ship registry request RPC: " + ex.Message)); } } } private static void RPC_ShipRecord(long sender, string shipGuid, bool found, Vector3 position, string zoneRaw) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) try { if (found && !string.IsNullOrWhiteSpace(shipGuid)) { Vector2i zone; Vector2i zone2 = (TryParseZone(zoneRaw, out zone) ? zone : GetZone(position)); ShipRecord shipRecord = new ShipRecord { ShipGuid = shipGuid, Position = position, Zone = zone2, UpdatedUnixSeconds = DateTimeOffset.UtcNow.ToUnixTimeSeconds() }; UpsertClientCache(shipRecord); ValheimRaftBedFixPlugin.DebugLog($"Received server ship registry record {shipRecord.ShipGuid} at {shipRecord.Position} zone {shipRecord.Zone.x},{shipRecord.Zone.y}"); } } catch (Exception ex) { ManualLogSource log = ValheimRaftBedFixPlugin.Log; if (log != null) { log.LogWarning((object)("Failed to handle ship registry response RPC: " + ex.Message)); } } } private static void UpsertClientCache(ShipRecord record) { if (record != null && !string.IsNullOrWhiteSpace(record.ShipGuid)) { ResetForWorldIfNeeded(); if (!Records.TryGetValue(record.ShipGuid, out var value) || value.UpdatedUnixSeconds <= record.UpdatedUnixSeconds) { Records[record.ShipGuid] = CloneRecord(record); } } } private static void UpsertServerRecord(ShipRecord record, bool saveSoon) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) if (!IsServer || record == null || string.IsNullOrWhiteSpace(record.ShipGuid)) { return; } LoadServerRegistryIfNeeded(); bool flag = false; if (!Records.TryGetValue(record.ShipGuid, out var value)) { value = new ShipRecord { ShipGuid = record.ShipGuid }; Records[record.ShipGuid] = value; flag = true; } else { Vector3 val = value.Position - record.Position; if (((Vector3)(ref val)).sqrMagnitude > 1f || value.Zone.x != record.Zone.x || value.Zone.y != record.Zone.y || value.UpdatedUnixSeconds <= 0) { flag = true; } } if (flag) { value.Position = record.Position; value.Zone = record.Zone; value.UpdatedUnixSeconds = Math.Max(record.UpdatedUnixSeconds, value.UpdatedUnixSeconds); _serverRegistryDirty = true; _nextServerRegistrySaveTime = (saveSoon ? Time.realtimeSinceStartup : (Time.realtimeSinceStartup + 10f)); ValheimRaftBedFixPlugin.DebugLog($"Server ship registry updated {record.ShipGuid} at {record.Position} zone {record.Zone.x},{record.Zone.y}"); } } private static ShipRecord CloneRecord(ShipRecord source) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) if (source == null) { return null; } return new ShipRecord { ShipGuid = source.ShipGuid, Position = source.Position, Zone = source.Zone, UpdatedUnixSeconds = source.UpdatedUnixSeconds }; } private static void ResetForWorldIfNeeded() { long currentWorldId = CurrentWorldId; if (_loadedWorldId != currentWorldId) { Records.Clear(); NextClientRegisterTime.Clear(); NextClientRequestTime.Clear(); _loadedWorldId = currentWorldId; _serverRegistryLoaded = false; _serverRegistryDirty = false; _nextServerRegistrySaveTime = 0f; } } private static void LoadServerRegistryIfNeeded() { //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) if (!IsServer) { return; } ResetForWorldIfNeeded(); if (_serverRegistryLoaded) { return; } _serverRegistryLoaded = true; _serverRegistryDirty = false; string registryPath = RegistryPath; if (!File.Exists(registryPath)) { return; } try { string[] array = File.ReadAllLines(registryPath); foreach (string text in array) { if (string.IsNullOrWhiteSpace(text)) { continue; } string[] array2 = text.Split('|'); if (array2.Length != 4) { continue; } string text2 = array2[0]; if (!string.IsNullOrWhiteSpace(text2) && TryParseVector3(array2[1], out var value)) { if (!TryParseZone(array2[2], out var zone)) { zone = GetZone(value); } if (!long.TryParse(array2[3], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { result = 0L; } Records[text2] = new ShipRecord { ShipGuid = text2, Position = value, Zone = zone, UpdatedUnixSeconds = result }; } } ValheimRaftBedFixPlugin.DebugLog($"Loaded {Records.Count} server ship registry records."); } catch (Exception ex) { ManualLogSource log = ValheimRaftBedFixPlugin.Log; if (log != null) { log.LogWarning((object)("Failed to load server ship registry: " + ex.Message)); } } } private static void SaveServerRegistry() { //IL_007f: Unknown result type (might be due to invalid IL or missing references) if (!IsServer || !_serverRegistryDirty) { return; } try { string registryPath = RegistryPath; string directoryName = Path.GetDirectoryName(registryPath); if (!string.IsNullOrEmpty(directoryName)) { Directory.CreateDirectory(directoryName); } List<string> list = new List<string>(); foreach (ShipRecord value in Records.Values) { if (value != null && !string.IsNullOrWhiteSpace(value.ShipGuid)) { list.Add(value.ShipGuid + "|" + FormatVector3(value.Position) + "|" + FormatZone(value.Zone) + "|" + value.UpdatedUnixSeconds.ToString(CultureInfo.InvariantCulture)); } } File.WriteAllLines(registryPath, list); _serverRegistryDirty = false; _nextServerRegistrySaveTime = 0f; } catch (Exception ex) { ManualLogSource log = ValheimRaftBedFixPlugin.Log; if (log != null) { log.LogWarning((object)("Failed to save server ship registry: " + ex.Message)); } } } private static long GetServerPeerId() { try { long? num = TryInvokeLongMethod(typeof(ZRoutedRpc), ZRoutedRpc.instance, "GetServerPeerID"); if (num.HasValue) { return num.Value; } num = TryInvokeLongMethod(typeof(ZRoutedRpc), ZRoutedRpc.instance, "GetServerPeerId"); if (num.HasValue) { return num.Value; } long? num2 = TryGetLongMember(typeof(ZNet), ZNet.instance, "m_serverPeerID"); if (num2.HasValue) { return num2.Value; } num2 = TryGetLongMember(typeof(ZNet), ZNet.instance, "m_serverPeerId"); if (num2.HasValue) { return num2.Value; } } catch (Exception ex) { ValheimRaftBedFixPlugin.DebugLog("Could not determine server peer id reflectively: " + ex.Message); } return 0L; } private static long? TryInvokeLongMethod(Type type, object instance, string methodName) { if (type == null || string.IsNullOrWhiteSpace(methodName)) { return null; } MethodInfo method = type.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method == null) { return null; } object obj = (method.IsStatic ? null : instance); if (!method.IsStatic && obj == null) { return null; } return ConvertToLong(method.Invoke(obj, null)); } private static long? TryGetLongMember(Type type, object instance, string memberName) { if (type == null || instance == null || string.IsNullOrWhiteSpace(memberName)) { return null; } FieldInfo field = type.GetField(memberName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { object obj = (field.IsStatic ? null : instance); return ConvertToLong(field.GetValue(obj)); } PropertyInfo property = type.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { MethodInfo getMethod = property.GetGetMethod(nonPublic: true); if (getMethod == null) { return null; } object obj2 = (getMethod.IsStatic ? null : instance); return ConvertToLong(property.GetValue(obj2, null)); } return null; } private static long? ConvertToLong(object value) { if (value == null) { return null; } if (value is long) { return (long)value; } if (value is int num) { return num; } if (value is uint num2) { return num2; } if (value is ulong num3 && num3 <= long.MaxValue) { return (long)num3; } return null; } private static Vector2i GetZone(Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return MobileZoneMath.GetZone(position); } private static string FormatVector3(Vector3 value) { return value.x.ToString(CultureInfo.InvariantCulture) + "," + value.y.ToString(CultureInfo.InvariantCulture) + "," + value.z.ToString(CultureInfo.InvariantCulture); } private static bool TryParseVector3(string raw, out Vector3 value) { //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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) value = Vector3.zero; if (string.IsNullOrWhiteSpace(raw)) { return false; } string[] array = raw.Split(','); if (array.Length != 3) { return false; } if (!float.TryParse(array[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return false; } if (!float.TryParse(array[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { return false; } if (!float.TryParse(array[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3)) { return false; } value = new Vector3(result, result2, result3); return true; } private static string FormatZone(Vector2i zone) { return $"{zone.x},{zone.y}"; } private static bool TryParseZone(string raw, out Vector2i zone) { zone = new Vector2i(0, 0); if (string.IsNullOrWhiteSpace(raw)) { return false; } string[] array = raw.Split(','); if (array.Length != 2) { return false; } if (!int.TryParse(array[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { return false; } if (!int.TryParse(array[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2)) { return false; } zone = new Vector2i(result, result2); return true; } } public static class MobileSpawnData { private const string Prefix = "ValheimRaftBedFix_MobileSpawn"; public const string BedGuidZdoKey = "ValheimRaftBedFix_BedGuid"; public const string ShipGuidZdoKey = "ValheimRaftBedFix_ShipGuid"; private static long WorldId { get { if (!((Object)(object)ZNet.instance != (Object)null)) { return 0L; } return ZNet.instance.GetWorldUID(); } } public static string BedGuidPlayerKey => string.Format("{0}_BedGuid_{1}", "ValheimRaftBedFix_MobileSpawn", WorldId); public static string ShipGuidPlayerKey => string.Format("{0}_ShipGuid_{1}", "ValheimRaftBedFix_MobileSpawn", WorldId); public static string LogoutKey => string.Format("{0}_IsMobileLogout_{1}", "ValheimRaftBedFix_MobileSpawn", WorldId); public static string OffsetKey => string.Format("{0}_LocalOffset_{1}", "ValheimRaftBedFix_MobileSpawn", WorldId); public static string ShipWorldPositionKey => string.Format("{0}_ShipWorldPosition_{1}", "ValheimRaftBedFix_MobileSpawn", WorldId); public static string ShipZoneKey => string.Format("{0}_ShipZone_{1}", "ValheimRaftBedFix_MobileSpawn", WorldId); public static bool HasAnyStoredMobileData(Player player) { if ((Object)(object)player == (Object)null) { return false; } if (TryGetMobileLogout(player, out var shipGuid, out var _)) { return true; } return TryGetBedGuid(player, out shipGuid); } public static void SetBed(Player player, string bedGuid) { if (!((Object)(object)player == (Object)null) && !string.IsNullOrWhiteSpace(bedGuid)) { player.m_customData[BedGuidPlayerKey] = bedGuid; } } public static bool TryGetBedGuid(Player player, out string bedGuid) { bedGuid = null; if ((Object)(object)player == (Object)null) { return false; } if (!player.m_customData.TryGetValue(BedGuidPlayerKey, out var value)) { return false; } if (string.IsNullOrWhiteSpace(value)) { return false; } bedGuid = value; return true; } public static void ClearStoredBed(Player player) { if (!((Object)(object)player == (Object)null)) { player.m_customData.Remove(BedGuidPlayerKey); } } public static void ClearStoredBedIfMatches(Player player, string bedGuid) { if (!((Object)(object)player == (Object)null) && !string.IsNullOrWhiteSpace(bedGuid) && TryGetBedGuid(player, out var bedGuid2) && string.Equals(bedGuid2, bedGuid, StringComparison.OrdinalIgnoreCase)) { ClearStoredBed(player); } } public static void ClearLocalStoredBedIfMatches(string bedGuid) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { ClearStoredBedIfMatches(localPlayer, bedGuid); } } public static void SetMobileLogout(Player player, string shipGuid, Vector3 localOffset) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) Vector3 shipWorldPosition = (((Object)(object)player != (Object)null) ? ((Component)player).transform.position : Vector3.zero); SetMobileLogout(player, shipGuid, localOffset, shipWorldPosition); } public static void SetMobileLogout(Player player, string shipGuid, Vector3 localOffset, Vector3 shipWorldPosition) { //IL_0044: 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_0065: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)player == (Object)null) && !string.IsNullOrWhiteSpace(shipGuid)) { player.m_customData[LogoutKey] = "true"; player.m_customData[ShipGuidPlayerKey] = shipGuid; player.m_customData[OffsetKey] = ToString(localOffset); player.m_customData[ShipWorldPositionKey] = ToString(shipWorldPosition); Vector2i zone = GetZone(shipWorldPosition); player.m_customData[ShipZoneKey] = $"{zone.x},{zone.y}"; } } public static void ClearMobileLogout(Player player) { if (!((Object)(object)player == (Object)null)) { player.m_customData[LogoutKey] = "false"; player.m_customData.Remove(ShipGuidPlayerKey); player.m_customData.Remove(OffsetKey); player.m_customData.Remove(ShipWorldPositionKey); player.m_customData.Remove(ShipZoneKey); } } public static void ClearAllMobileData(Player player) { if (!((Object)(object)player == (Object)null)) { ClearMobileLogout(player); ClearStoredBed(player); } } public static bool TryGetMobileLogout(Player player, out string shipGuid, out Vector3 localOffset) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) shipGuid = null; localOffset = Vector3.zero; if ((Object)(object)player == (Object)null) { return false; } if (!player.m_customData.TryGetValue(LogoutKey, out var value)) { return false; } if (!string.Equals(value, "true", StringComparison.OrdinalIgnoreCase)) { return false; } if (!player.m_customData.TryGetValue(ShipGuidPlayerKey, out shipGuid)) { return false; } if (string.IsNullOrWhiteSpace(shipGuid)) { return false; } if (!player.m_customData.TryGetValue(OffsetKey, out var value2)) { return false; } return TryParseVector3(value2, out localOffset); } public static bool TryGetStoredShipPosition(Player player, out Vector3 position) { //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) position = Vector3.zero; if ((Object)(object)player == (Object)null) { return false; } if (!player.m_customData.TryGetValue(ShipWorldPositionKey, out var value)) { return false; } return TryParseVector3(value, out position); } public static bool TryGetStoredShipZone(Player player, out Vector2i zone) { zone = new Vector2i(0, 0); if ((Object)(object)player == (Object)null) { return false; } if (!player.m_customData.TryGetValue(ShipZoneKey, out var value)) { return false; } return TryParseZone(value, out zone); } public static string GetOrCreateBedGuid(Bed bed) { if ((Object)(object)bed == (Object)null) { return null; } ZNetView component = ((Component)bed).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { return null; } ZDO zDO = component.GetZDO(); if (zDO == null) { return null; } string text = zDO.GetString("ValheimRaftBedFix_BedGuid", ""); if (string.IsNullOrWhiteSpace(text)) { text = Guid.NewGuid().ToString("N"); zDO.Set("ValheimRaftBedFix_BedGuid", text); ValheimRaftBedFixPlugin.DebugLog("Generated bed GUID " + text); } return text; } public static string GetExistingBedGuid(Bed bed) { if ((Object)(object)bed == (Object)null) { return null; } ZNetView component = ((Component)bed).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { return null; } ZDO zDO = component.GetZDO(); if (zDO == null) { return null; } string @string = zDO.GetString("ValheimRaftBedFix_BedGuid", ""); if (!string.IsNullOrWhiteSpace(@string)) { return @string; } return null; } public static bool TryResolveBedByGuid(string bedGuid, out Bed bed, out ZNetView bedView) { bed = null; bedView = null; if (string.IsNullOrWhiteSpace(bedGuid)) { return false; } Bed[] array = Object.FindObjectsByType<Bed>((FindObjectsSortMode)0); foreach (Bed val in array) { if ((Object)(object)val == (Object)null) { continue; } ZNetView component = ((Component)val).GetComponent<ZNetView>(); if (!((Object)(object)component == (Object)null) && component.IsValid()) { ZDO zDO = component.GetZDO(); if (zDO != null && string.Equals(zDO.GetString("ValheimRaftBedFix_BedGuid", ""), bedGuid, StringComparison.OrdinalIgnoreCase)) { bed = val; bedView = component; return true; } } } return false; } public static bool TryFindNearbyBed(Player player, float radius, out Bed nearbyBed) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_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_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) nearbyBed = null; if ((Object)(object)player == (Object)null) { return false; } Vector3 position = ((Component)player).transform.position; float num = radius * radius; float num2 = float.MaxValue; Bed[] array = Object.FindObjectsByType<Bed>((FindObjectsSortMode)0); foreach (Bed val in array) { if ((Object)(object)val == (Object)null) { continue; } ZNetView component = ((Component)val).GetComponent<ZNetView>(); if (!((Object)(object)component == (Object)null) && component.IsValid()) { Vector3 val2 = ((Component)val).transform.position - position; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (!(sqrMagnitude > num) && !(sqrMagnitude >= num2)) { num2 = sqrMagnitude; nearbyBed = val; } } } return (Object)(object)nearbyBed != (Object)null; } public static string Vector3ToString(Vector3 value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return ToString(value); } public static bool TryParseStoredVector3(string raw, out Vector3 value) { return TryParseVector3(raw, out value); } public static string ZoneToString(Vector2i zone) { return $"{zone.x},{zone.y}"; } public static bool TryParseStoredZone(string raw, out Vector2i zone) { return TryParseZone(raw, out zone); } private static Vector2i GetZone(Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return MobileZoneMath.GetZone(position); } private static string ToString(Vector3 v) { return v.x.ToString(CultureInfo.InvariantCulture) + "," + v.y.ToString(CultureInfo.InvariantCulture) + "," + v.z.ToString(CultureInfo.InvariantCulture); } private static bool TryParseVector3(string raw, out Vector3 value) { //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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) value = Vector3.zero; if (string.IsNullOrWhiteSpace(raw)) { return false; } string[] array = raw.Split(','); if (array.Length != 3) { return false; } if (!float.TryParse(array[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return false; } if (!float.TryParse(array[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { return false; } if (!float.TryParse(array[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3)) { return false; } value = new Vector3(result, result2, result3); return true; } private static bool TryParseZone(string raw, out Vector2i zone) { zone = new Vector2i(0, 0); if (string.IsNullOrWhiteSpace(raw)) { return false; } string[] array = raw.Split(','); if (array.Length != 2) { return false; } if (!int.TryParse(array[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { return false; } if (!int.TryParse(array[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2)) { return false; } zone = new Vector2i(result, result2); return true; } } public sealed class MobileSpawnState { public long BedID; public bool IsMobileLogout; public Vector3 LocalOffset; } public readonly struct Vector2i { public readonly int x; public readonly int y; public Vector2i(int x, int y) { this.x = x; this.y = y; } public override string ToString() { return x.ToString(CultureInfo.InvariantCulture) + "," + y.ToString(CultureInfo.InvariantCulture); } } internal static class MobileZoneMath { private const float ZoneSize = 64f; public static Vector2i GetZone(Vector3 position) { //IL_0000: 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) int x = Mathf.FloorToInt((position.x + 32f) / 64f); int y = Mathf.FloorToInt((position.z + 32f) / 64f); return new Vector2i(x, y); } } [HarmonyPatch(typeof(WearNTear), "Destroy")] internal static class BedDestroyedPatch { private static void Prefix(WearNTear __instance) { if ((Object)(object)__instance == (Object)null) { return; } Bed val = ((Component)__instance).GetComponent<Bed>() ?? ((Component)__instance).GetComponentInParent<Bed>() ?? ((Component)__instance).GetComponentInChildren<Bed>(); if (!((Object)(object)val == (Object)null)) { string existingBedGuid = MobileSpawnData.GetExistingBedGuid(val); if (!string.IsNullOrWhiteSpace(existingBedGuid)) { MobileSpawnData.ClearLocalStoredBedIfMatches(existingBedGuid); ValheimRaftBedFixPlugin.DebugLog("Destroyed mobile bed GUID " + existingBedGuid + "; cleared local stored bed reference if present."); } } } } [BepInPlugin("DarkestReponse.valheimraftbedfix", "ValheimRaftBedFix", "0.1.5")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class ValheimRaftBedFixPlugin : BaseUnityPlugin { private enum RestoreState { None, Waiting, Restoring, Success, FailedFallback } public const string ModGUID = "DarkestReponse.valheimraftbedfix"; public const string ModName = "ValheimRaftBedFix"; public const string ModVersion = "0.1.5"; public static ConfigEntry<bool> DebugMode; public static ConfigEntry<int> MaxRestoreRetries; public static ConfigEntry<float> RetryDelaySeconds; public static ConfigEntry<bool> EnableCenterMessages; public static ConfigEntry<float> HoldingHeight; public static ConfigEntry<float> PostRestoreTrackingBlockSeconds; public static ConfigEntry<int> StableFramesRequired; public static ConfigEntry<float> ServerRegistryScanInterval; public static readonly Vector3 TemporaryHoldingPosition = new Vector3(0f, 10000f, 0f); private readonly Harmony _harmony = new Harmony("DarkestReponse.valheimraftbedfix"); private RestoreState _restoreState; private float _nextShipSampleTime; private float _nextRestoreAttemptTime; private float _nextRestoreMessageTime; private float _nextZoneLoadPulseTime; private float _nextServerRegistryScanTime; private float _trackingBlockedUntil; private int _restoreAttempts; private int _stableFrames; private bool _hasLastRootY; private float _lastRootY; private string _activeShipGuid; private Vector3 _activeLocalOffset; private Vector3 _activeHoldPosition; private Quaternion _lastSafeRotation = Quaternion.identity; private Rigidbody _suspendedBody; private bool _savedUseGravity; private bool _savedGodMode; private bool _savedHadGodModeField; private FieldInfo _godModeField; public static bool RestoreInProgress { get; set; } public static ValheimRaftBedFixPlugin Instance { get; private set; } public static ManualLogSource Log { get; private set; } private void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; DebugMode = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugMode", false, "Enable verbose debug logging."); MaxRestoreRetries = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaxRestoreRetries", 120, "Maximum number of ship restore retry attempts."); RetryDelaySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "RetryDelaySeconds", 0.5f, "Delay in seconds between ship restore attempts."); EnableCenterMessages = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableCenterMessages", true, "Show center-screen status messages during ship restoration."); HoldingHeight = ((BaseUnityPlugin)this).Config.Bind<float>("General", "HoldingHeight", 10000f, "Y height used while holding a player above the stored ship zone."); PostRestoreTrackingBlockSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "PostRestoreTrackingBlockSeconds", 8f, "Seconds to block logout tracking after restore/fallback."); StableFramesRequired = ((BaseUnityPlugin)this).Config.Bind<int>("General", "StableFramesRequired", 3, "Number of consecutive stable restore checks required before placing the player back on the boat."); ServerRegistryScanInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Server", "ServerRegistryScanInterval", 5f, "Seconds between server-side scans of loaded mobile ships for the persistent ship registry."); ((BaseUnityPlugin)this).Logger.LogInfo((object)"ValheimRaftBedFix loading"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)("ValheimRaftBedFix loaded | " + $"Debug={DebugMode.Value} | " + $"MaxRetries={MaxRestoreRetries.Value} | " + $"RetryDelay={RetryDelaySeconds.Value:F2}s | " + $"CenterMessages={EnableCenterMessages.Value}")); } private void Update() { MobileShipRegistry.Update(); UpdateServerShipRegistry(); Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && !TryHandlePendingMobileRestore(localPlayer) && !(Time.time < _nextShipSampleTime)) { _nextShipSampleTime = Time.time + 1f; SampleShipLogoutState(localPlayer); } } private void UpdateServerShipRegistry() { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || Time.time < _nextServerRegistryScanTime) { return; } float num = ((ServerRegistryScanInterval != null) ? Mathf.Max(1f, ServerRegistryScanInterval.Value) : 5f); _nextServerRegistryScanTime = Time.time + num; try { MobileShipDetector.ScanLoadedShipsForRegistry(); } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("Server ship registry scan failed: " + ex.Message)); } } } private bool TryHandlePendingMobileRestore(Player player) { //IL_003e: 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_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013b: 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_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0149: 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) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) if (!MobileSpawnData.TryGetMobileLogout(player, out var shipGuid, out var localOffset)) { if (_restoreState != 0) { FinishRestore(player, RestoreState.FailedFallback); } RestoreInProgress = false; return false; } if (_restoreState == RestoreState.None || !string.Equals(_activeShipGuid, shipGuid, StringComparison.OrdinalIgnoreCase)) { BeginRestore(player, shipGuid, localOffset); } RestoreInProgress = true; ApplyPlayerSuspension(player, _activeHoldPosition, forceTeleport: false); if (Time.time >= _nextRestoreMessageTime) { ShowCenterMessage("Waiting for mobile boat to load..."); _nextRestoreMessageTime = Time.time + 3f; } ForceLoadStoredShipZone(player, shipGuid); if (Time.time < _nextRestoreAttemptTime) { return true; } _nextRestoreAttemptTime = Time.time + Mathf.Max(0.1f, RetryDelaySeconds.Value); _restoreAttempts++; DebugLog($"Ship restore attempt {_restoreAttempts}/{MaxRestoreRetries.Value} " + "for ship GUID " + shipGuid); if (!MobileShipDetector.TryResolveShipByGuid(shipGuid, out var vehicleRoot, out var vehicleView)) { if (_restoreAttempts >= MaxRestoreRetries.Value) { RestoreViaFallback(player); } return true; } _restoreState = RestoreState.Restoring; MobileShipDetector.RegisterShip(vehicleView, vehicleRoot); if (!IsVehicleStableForRestore(vehicleRoot)) { return true; } Vector3 position = vehicleRoot.TransformPoint(localOffset); position = MakeRestorePositionSafe(position); Quaternion rotation = vehicleRoot.rotation; DebugLog($"Restoring player to ship GUID {shipGuid} at {position}"); TeleportPlayer(player, position, rotation); MobileSpawnData.ClearMobileLogout(player); ShowCenterMessage("Restored position on mobile boat."); _trackingBlockedUntil = Time.time + PostRestoreTrackingBlockSeconds.Value; FinishRestore(player, RestoreState.Success); return true; } private void BeginRestore(Player player, string shipGuid, Vector3 localOffset) { //IL_000f: 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_0055: 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_0068: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) _restoreState = RestoreState.Waiting; _activeShipGuid = shipGuid; _activeLocalOffset = localOffset; _restoreAttempts = 0; _stableFrames = 0; _hasLastRootY = false; _nextRestoreAttemptTime = 0f; _nextRestoreMessageTime = 0f; _nextZoneLoadPulseTime = 0f; MobileShipRegistry.RequestShipRecord(shipGuid); _activeHoldPosition = BuildHoldingPosition(player, shipGuid); RestoreInProgress = true; ApplyPlayerSuspension(player, _activeHoldPosition, forceTeleport: true); ShowCenterMessage("Waiting for mobile boat to load..."); DebugLog("Starting mobile restore for ship GUID " + shipGuid + "; " + $"holding at {_activeHoldPosition}, local offset {localOffset}"); } private Vector3 BuildHoldingPosition(Player player, string shipGuid) { //IL_000c: 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_0022: 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_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) if (MobileShipDetector.TryGetRegisteredShipPosition(shipGuid, out var position, out var _)) { return new Vector3(position.x, HoldingHeight.Value, position.z); } if (MobileSpawnData.TryGetStoredShipPosition(player, out var position2)) { return new Vector3(position2.x, HoldingHeight.Value, position2.z); } return new Vector3(TemporaryHoldingPosition.x, HoldingHeight.Value, TemporaryHoldingPosition.z); } private void ForceLoadStoredShipZone(Player player, string shipGuid) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_003a: 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) //IL_0056: 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_005d: 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_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0073: 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 (Time.time < _nextZoneLoadPulseTime) { return; } _nextZoneLoadPulseTime = Time.time + 2f; MobileShipRegistry.RequestShipRecord(shipGuid); Vector3 activeHoldPosition = _activeHoldPosition; Vector3 position2; if (MobileShipDetector.TryGetRegisteredShipPosition(shipGuid, out var position, out var _)) { ((Vector3)(ref activeHoldPosition))..ctor(position.x, _activeHoldPosition.y, position.z); Vector3 val = activeHoldPosition - _activeHoldPosition; if (((Vector3)(ref val)).sqrMagnitude > 4f) { _activeHoldPosition = activeHoldPosition; ApplyPlayerSuspension(player, _activeHoldPosition, forceTeleport: true); } } else if (MobileSpawnData.TryGetStoredShipPosition(player, out position2)) { ((Vector3)(ref activeHoldPosition))..ctor(position2.x, _activeHoldPosition.y, position2.z); } TrySetNetworkReferencePosition(activeHoldPosition); TryPulseZoneSystem(activeHoldPosition); } private bool IsVehicleStableForRestore(Transform vehicleRoot) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)vehicleRoot == (Object)null) { return false; } if (!MobileShipDetector.IsShipStable(vehicleRoot)) { _stableFrames = 0; _hasLastRootY = false; return false; } float y = vehicleRoot.position.y; if (_hasLastRootY && Mathf.Abs(y - _lastRootY) > 0.2f) { _stableFrames = 0; _lastRootY = y; return false; } _lastRootY = y; _hasLastRootY = true; _stableFrames++; return _stableFrames >= Mathf.Max(1, StableFramesRequired.Value); } private void RestoreViaFallback(Player player) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0055: 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) if (TryRestoreToStoredBed(player)) { MobileSpawnData.ClearMobileLogout(player); _trackingBlockedUntil = Time.time + PostRestoreTrackingBlockSeconds.Value; FinishRestore(player, RestoreState.Success); return; } ShowCenterMessage("Could not locate your mobile boat. Returning to safe fallback."); Vector3 safeOriginFallback = GetSafeOriginFallback(); MobileSpawnData.ClearMobileLogout(player); if (MobileSpawnData.TryGetBedGuid(player, out var _)) { MobileSpawnData.ClearStoredBed(player); } TeleportPlayer(player, safeOriginFallback, Quaternion.identity); _trackingBlockedUntil = Time.time + PostRestoreTrackingBlockSeconds.Value; FinishRestore(player, RestoreState.FailedFallback); } private bool TryRestoreToStoredBed(Player player) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: 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) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) if (!MobileSpawnData.TryGetBedGuid(player, out var bedGuid)) { return false; } if (!MobileSpawnData.TryResolveBedByGuid(bedGuid, out var bed, out var _)) { DebugLog("Stored bed GUID " + bedGuid + " was not found during validation."); return false; } Vector3 position = MakeRestorePositionSafe(((Component)bed).transform.position + Vector3.up * 1.5f); Quaternion rotation = ((Component)bed).transform.rotation; DebugLog("Fallback bed restore resolved bed GUID " + bedGuid + " " + $"at {((Component)bed).transform.position}"); TeleportPlayer(player, position, rotation); ShowCenterMessage("Restored to mobile bed."); return true; } private void FinishRestore(Player player, RestoreState finalState) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) _restoreState = finalState; RestoreInProgress = false; ReleasePlayerSuspension(player); _activeShipGuid = null; _activeLocalOffset = Vector3.zero; _stableFrames = 0; _hasLastRootY = false; _nextRestoreAttemptTime = 0f; _nextRestoreMessageTime = 0f; _nextZoneLoadPulseTime = 0f; _restoreState = RestoreState.None; } private void ApplyPlayerSuspension(Player player, Vector3 holdPosition, bool forceTeleport) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0072: 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_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { return; } Rigidbody component = ((Component)player).GetComponent<Rigidbody>(); if ((Object)(object)_suspendedBody == (Object)null && (Object)(object)component != (Object)null) { _suspendedBody = component; _savedUseGravity = component.useGravity; } if ((Object)(object)component != (Object)null) { component.useGravity = false; component.linearVelocity = Vector3.zero; component.angularVelocity = Vector3.zero; } SetPlayerGodMode(player, enabled: true); if (!forceTeleport) { Vector3 val = ((Component)player).transform.position - holdPosition; if (!(((Vector3)(ref val)).sqrMagnitude > 625f)) { ((Component)player).transform.position = holdPosition; if ((Object)(object)component != (Object)null) { component.position = holdPosition; } return; } } TeleportPlayer(player, holdPosition, ((Component)player).transform.rotation); } private void ReleasePlayerSuspension(Player player) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_suspendedBody != (Object)null) { _suspendedBody.useGravity = _savedUseGravity; _suspendedBody.linearVelocity = Vector3.zero; _suspendedBody.angularVelocity = Vector3.zero; _suspendedBody = null; } if ((Object)(object)player != (Object)null) { SetPlayerGodMode(player, enabled: false); } } private void SetPlayerGodMode(Player player, bool enabled) { if ((Object)(object)player == (Object)null) { return; } try { if ((object)_godModeField == null) { _godModeField = typeof(Character).GetField("m_godMode", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } if (_godModeField == null || _godModeField.FieldType != typeof(bool)) { return; } if (enabled) { if (!_savedHadGodModeField) { _savedGodMode = (bool)_godModeField.GetValue(player); _savedHadGodModeField = true; } _godModeField.SetValue(player, true); } else if (_savedHadGodModeField) { _godModeField.SetValue(player, _savedGodMode); _savedHadGodModeField = false; } } catch (Exception ex) { DebugLog("Could not toggle temporary god mode: " + ex.Message); } } private static Vector3 MakeRestorePositionSafe(Vector3 position) { //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_001e: 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) float num = GetWaterLevel(position) + 1.25f; if (position.y < num) { position.y = num; } return position + Vector3.up * 0.35f; } public static Vector3 GetSafeOriginFallback() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: 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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) Vector3 zero = Vector3.zero; float num = 0f; if ((Object)(object)ZoneSystem.instance != (Object)null) { num = ZoneSystem.instance.GetGroundHeight(zero); } float waterLevel = GetWaterLevel(zero); return new Vector3(0f, Mathf.Max(num, waterLevel) + 5f, 0f); } private static float GetWaterLevel(Vector3 position) { if ((Object)(object)ZoneSystem.instance == (Object)null) { return 30f; } try { FieldInfo field = typeof(ZoneSystem).GetField("m_waterLevel", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null && field.FieldType == typeof(float)) { return (float)field.GetValue(ZoneSystem.instance); } } catch { } return 30f; } private static void TeleportPlayer(Player player, Vector3 position, Quaternion rotation) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)player == (Object)null)) { ((Character)player).TeleportTo(position, rotation, true); } } private static void TrySetNetworkReferencePosition(Vector3 position) { //IL_000a: 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) TryInvokeReferencePosition(ZNet.instance, "SetReferencePosition", position); TryInvokeReferencePosition(ZNetScene.instance, "SetReferencePosition", position); } private static void TryPulseZoneSystem(Vector3 position) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZoneSystem.instance == (Object)null) { return; } TryInvokeReferencePosition(ZoneSystem.instance, "SetReferencePosition", position); try { ZoneSystem.instance.GetGroundHeight(position); } catch { } } private static void TryInvokeReferencePosition(object instance, string methodName, Vector3 position) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) if (instance == null) { return; } try { instance.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(Vector3) }, null)?.Invoke(instance, new object[1] { position }); } catch { } } private void SampleShipLogoutState(Player player) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: 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) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || RestoreInProgress || _restoreState != 0 || Time.time < _trackingBlockedUntil) { return; } try { if (!MobileShipDetector.TryFindVehicleRoot(player, out var vehicleRoot, out var vehicleView)) { return; } if (!MobileShipDetector.IsShipStable(vehicleRoot)) { DebugLog("Ship unstable; skipping logout sampling."); return; } string orCreateShipGuid = MobileShipDetector.GetOrCreateShipGuid(vehicleView); if (string.IsNullOrWhiteSpace(orCreateShipGuid)) { return; } Vector3 val = vehicleRoot.InverseTransformPoint(((Component)player).transform.position); if (((Vector3)(ref val)).sqrMagnitude < 0.0001f) { Vector3 val2 = vehicleRoot.position - ((Component)player).transform.position; if (((Vector3)(ref val2)).sqrMagnitude < 0.0001f) { DebugLog("Skipping suspicious zero-offset ship sample."); return; } } MobileShipDetector.RegisterShip(vehicleView, vehicleRoot); MobileSpawnData.SetMobileLogout(player, orCreateShipGuid, val, vehicleRoot.position); if (MobileSpawnData.TryFindNearbyBed(player, 10f, out var nearbyBed)) { string orCreateBedGuid = MobileSpawnData.GetOrCreateBedGuid(nearbyBed); if (!string.IsNullOrWhiteSpace(orCreateBedGuid)) { MobileSpawnData.SetBed(player, orCreateBedGuid); } } DebugLog("Tracked mobile logout on ship GUID " + orCreateShipGuid + ", " + $"local offset {val}, ship position {vehicleRoot.position}"); } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)$"Ship logout tracking failed: {arg}"); } } } public static bool ShouldSuppressDamage(Character character) { if (!RestoreInProgress) { return false; } if ((Object)(object)character == (Object)null) { return false; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } return character == localPlayer; } public static void DebugLog(string message) { if (DebugMode != null && DebugMode.Value) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)message); } } } public static void ShowCenterMessage(string message) { if (EnableCenterMessages == null || EnableCenterMessages.Value) { MessageHud instance = MessageHud.instance; if (instance != null) { instance.ShowMessage((MessageType)2, message, 0, (Sprite)null, false); } } } private void OnDestroy() { ReleasePlayerSuspension(Player.m_localPlayer); MobileShipRegistry.FlushServerRegistryIfDirty(); _harmony.UnpatchSelf(); Instance = null; Log = null; } } [HarmonyPatch(typeof(Character), "Damage", new Type[] { typeof(HitData) })] internal static class RestoreDamageSuppressionPatch { private static bool Prefix(Character __instance) { return !ValheimRaftBedFixPlugin.ShouldSuppressDamage(__instance); } } } namespace ValheimRaftBedFix.Patches { [HarmonyPatch(typeof(Bed), "Interact")] public static class BedClaimPatch { [HarmonyPostfix] private static void Postfix(Bed __instance, Humanoid human, bool repeat, bool alt, bool __result) { //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) ManualLogSource log = ValheimRaftBedFixPlugin.Log; if (log != null) { log.LogInfo((object)"BedClaimPatch fired"); } if (repeat) { ManualLogSource log2 = ValheimRaftBedFixPlugin.Log; if (log2 != null) { log2.LogInfo((object)"BedClaimPatch exit: repeat interaction"); } return; } if ((Object)(object)__instance == (Object)null) { ManualLogSource log3 = ValheimRaftBedFixPlugin.Log; if (log3 != null) { log3.LogWarning((object)"BedClaimPatch exit: __instance was null"); } return; } if ((Object)(object)Player.m_localPlayer == (Object)null) { ManualLogSource log4 = ValheimRaftBedFixPlugin.Log; if (log4 != null) { log4.LogWarning((object)"BedClaimPatch exit: Player.m_localPlayer was null"); } return; } if ((Object)(object)human != (Object)(object)Player.m_localPlayer) { ManualLogSource log5 = ValheimRaftBedFixPlugin.Log; if (log5 != null) { log5.LogInfo((object)"BedClaimPatch exit: human != localPlayer"); } return; } ZNetView component = ((Component)__instance).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null) { ManualLogSource log6 = ValheimRaftBedFixPlugin.Log; if (log6 != null) { log6.LogWarning((object)"BedClaimPatch exit: no ZNetView"); } return; } if (!component.IsValid()) { ManualLogSource log7 = ValheimRaftBedFixPlugin.Log; if (log7 != null) { log7.LogWarning((object)"BedClaimPatch exit: invalid ZNetView"); } return; } ZDO zDO = component.GetZDO(); if (zDO == null) { ManualLogSource log8 = ValheimRaftBedFixPlugin.Log; if (log8 != null) { log8.LogWarning((object)"BedClaimPatch exit: ZDO was null"); } return; } string text = zDO.GetString("ValheimRaftBedFix_BedGuid", ""); if (string.IsNullOrWhiteSpace(text)) { text = Guid.NewGuid().ToString("N"); zDO.Set("ValheimRaftBedFix_BedGuid", text); ManualLogSource log9 = ValheimRaftBedFixPlugin.Log; if (log9 != null) { log9.LogInfo((object)$"Generated new persistent bed GUID {text} for ZDO {zDO.m_uid}"); } } else { ManualLogSource log10 = ValheimRaftBedFixPlugin.Log; if (log10 != null) { log10.LogInfo((object)$"Found existing persistent bed GUID {text} on ZDO {zDO.m_uid}"); } } MobileSpawnData.SetBed(Player.m_localPlayer, text); ManualLogSource log11 = ValheimRaftBedFixPlugin.Log; if (log11 != null) { log11.LogInfo((object)("Saved player mobile bed GUID " + text)); } } } internal static class RespawnPatch { } }