using 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(); 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() ?? ((Component)vehicleRoot).GetComponentInParent(); 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((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((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((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(); for (int i = 0; i < components.Length; i++) { ConsiderView(components[i]); } components = ((Component)val).GetComponentsInParent(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(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(); 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() != (Object)null) { num += 60; } else if ((Object)(object)((Component)root).GetComponentInParent() != (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() != (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() ?? ((Component)transform).GetComponentInParent() ?? ((Component)transform).GetComponentInChildren(); 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 predicate) { if ((Object)(object)transform == (Object)null) { return false; } Component[] array = (includeChildren ? ((Component)transform).GetComponentsInChildren(true) : ((Component)transform).GetComponents()); 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 Records = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary NextClientRegisterTime = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary NextClientRequestTime = new Dictionary(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("ValheimRaftBedFix_RPC_RegisterShip", (Action)RPC_RegisterShip); ZRoutedRpc.instance.Register("ValheimRaftBedFix_RPC_RequestShip", (Action)RPC_RequestShip); ZRoutedRpc.instance.Register("ValheimRaftBedFix_RPC_ShipRecord", (Method)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 list = new List(); 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(); 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(); 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((FindObjectsSortMode)0); foreach (Bed val in array) { if ((Object)(object)val == (Object)null) { continue; } ZNetView component = ((Component)val).GetComponent(); 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((FindObjectsSortMode)0); foreach (Bed val in array) { if ((Object)(object)val == (Object)null) { continue; } ZNetView component = ((Component)val).GetComponent(); 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() ?? ((Component)__instance).GetComponentInParent() ?? ((Component)__instance).GetComponentInChildren(); 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 DebugMode; public static ConfigEntry MaxRestoreRetries; public static ConfigEntry RetryDelaySeconds; public static ConfigEntry EnableCenterMessages; public static ConfigEntry HoldingHeight; public static ConfigEntry PostRestoreTrackingBlockSeconds; public static ConfigEntry StableFramesRequired; public static ConfigEntry 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("General", "DebugMode", false, "Enable verbose debug logging."); MaxRestoreRetries = ((BaseUnityPlugin)this).Config.Bind("General", "MaxRestoreRetries", 120, "Maximum number of ship restore retry attempts."); RetryDelaySeconds = ((BaseUnityPlugin)this).Config.Bind("General", "RetryDelaySeconds", 0.5f, "Delay in seconds between ship restore attempts."); EnableCenterMessages = ((BaseUnityPlugin)this).Config.Bind("General", "EnableCenterMessages", true, "Show center-screen status messages during ship restoration."); HoldingHeight = ((BaseUnityPlugin)this).Config.Bind("General", "HoldingHeight", 10000f, "Y height used while holding a player above the stored ship zone."); PostRestoreTrackingBlockSeconds = ((BaseUnityPlugin)this).Config.Bind("General", "PostRestoreTrackingBlockSeconds", 8f, "Seconds to block logout tracking after restore/fallback."); StableFramesRequired = ((BaseUnityPlugin)this).Config.Bind("General", "StableFramesRequired", 3, "Number of consecutive stable restore checks required before placing the player back on the boat."); ServerRegistryScanInterval = ((BaseUnityPlugin)this).Config.Bind("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(); 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(); 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 { } }