Decompiled source of BoatTracking v1.0.1

BepInEx\plugins\BoatTracking.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("BoatTracking")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+a24d97e39e7c8896a1756e1f1d54a2836bc54e87")]
[assembly: AssemblyProduct("BoatTracking")]
[assembly: AssemblyTitle("BoatTracking")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BoatTracking
{
	[BepInPlugin("matthb1.boattracking", "BoatTracking", "1.0.1")]
	public class BoatTrackingPlugin : BaseUnityPlugin
	{
		public const string GUID = "matthb1.boattracking";

		public const string NAME = "BoatTracking";

		public const string VERSION = "1.0.1";

		public const int ProtocolVersion = 1;

		public const string ZdoNameKey = "BoatTracking.Name";

		public const string RpcSync = "BoatTracking.Sync";

		public const string RpcRename = "BoatTracking.Rename";

		public const string RpcRequestSync = "BoatTracking.RequestSync";

		internal static BoatTrackingPlugin Instance;

		internal static ConfigEntry<bool> ShowPins;

		internal static ConfigEntry<bool> ShowUnnamed;

		internal static ConfigEntry<float> SyncInterval;

		internal static ConfigEntry<int> MaxNameLength;

		internal static ManualLogSource Log;

		private void Awake()
		{
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			ShowPins = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowPins", true, "Show ship pins on the minimap and world map.");
			ShowUnnamed = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowUnnamed", true, "Show pins for ships that have not been renamed (label = ship type).");
			SyncInterval = ((BaseUnityPlugin)this).Config.Bind<float>("General", "SyncInterval", 3f, "Seconds between server ship-position syncs (1-30).");
			MaxNameLength = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaxNameLength", 24, "Max characters for a ship name (1-40).");
			((BaseUnityPlugin)this).Logger.LogInfo((object)"BoatTracking 1.0.1 loaded - name ships with hold E; server syncs map pins.");
			new Harmony("matthb1.boattracking").PatchAll();
		}

		private void Update()
		{
			ShipSync.Tick(Time.deltaTime);
		}
	}
	internal static class ShipCatalog
	{
		internal readonly struct PrefabInfo
		{
			public readonly string Prefab;

			public readonly string Label;

			public readonly int Hash;

			public PrefabInfo(string prefab, string label)
			{
				Prefab = prefab;
				Label = label;
				Hash = StringExtensionMethods.GetStableHashCode(prefab);
			}
		}

		internal static readonly PrefabInfo[] Prefabs = new PrefabInfo[4]
		{
			new PrefabInfo("Raft", "Raft"),
			new PrefabInfo("Karve", "Karve"),
			new PrefabInfo("VikingShip", "Longship"),
			new PrefabInfo("VikingShip_Ashlands", "Drakkar")
		};

		private static readonly Dictionary<int, PrefabInfo> ByHash = BuildLookup();

		private static Dictionary<int, PrefabInfo> BuildLookup()
		{
			Dictionary<int, PrefabInfo> dictionary = new Dictionary<int, PrefabInfo>();
			PrefabInfo[] prefabs = Prefabs;
			for (int i = 0; i < prefabs.Length; i++)
			{
				PrefabInfo value = prefabs[i];
				dictionary[value.Hash] = value;
			}
			return dictionary;
		}

		internal static bool TryGet(int prefabHash, out PrefabInfo info)
		{
			return ByHash.TryGetValue(prefabHash, out info);
		}

		internal static string DisplayName(ZDO zdo, PrefabInfo info)
		{
			string text = zdo.GetString("BoatTracking.Name", string.Empty);
			if (!string.IsNullOrWhiteSpace(text))
			{
				return text.Trim();
			}
			return info.Label;
		}

		internal static string DisplayName(string custom, string typeLabel)
		{
			if (!string.IsNullOrWhiteSpace(custom))
			{
				return custom.Trim();
			}
			return typeLabel;
		}

		internal static Ship? FindShipFrom(Component? component)
		{
			if (!Object.op_Implicit((Object)(object)component))
			{
				return null;
			}
			Ship val = (Ship)(object)((component is Ship) ? component : null);
			if (val != null)
			{
				return val;
			}
			Ship componentInParent = component.GetComponentInParent<Ship>();
			if (Object.op_Implicit((Object)(object)componentInParent))
			{
				return componentInParent;
			}
			Transform root = component.transform.root;
			if (!Object.op_Implicit((Object)(object)root))
			{
				return null;
			}
			return ((Component)root).GetComponentInChildren<Ship>();
		}
	}
	internal static class ShipPins
	{
		private sealed class MarkerState
		{
			public Vector3 Pos;

			public string Name = string.Empty;

			public int PrefabHash;

			public GameObject? Marker;
		}

		private static readonly Dictionary<ZDOID, MarkerState> Markers = new Dictionary<ZDOID, MarkerState>();

		private static readonly Dictionary<int, Sprite?> SpriteCache = new Dictionary<int, Sprite>();

		private static Sprite? _fallbackSprite;

		private static int _lastLoggedCount = -1;

		private static Font? _mapFont;

		private static bool _loggedLabelOnce;

		internal static void Clear()
		{
			foreach (MarkerState value in Markers.Values)
			{
				DestroyMarker(value);
			}
			Markers.Clear();
			_lastLoggedCount = -1;
		}

		internal static void Apply(IReadOnlyList<ShipSnapshot> ships)
		{
			try
			{
				ApplyInternal(ships);
			}
			catch (Exception arg)
			{
				BoatTrackingPlugin.Log.LogError((object)$"ShipPins.Apply failed: {arg}");
			}
		}

		private static void ApplyInternal(IReadOnlyList<ShipSnapshot> ships)
		{
			//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)
			//IL_0035: 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_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: 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_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: 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)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			if (!BoatTrackingPlugin.ShowPins.Value)
			{
				Clear();
				return;
			}
			HashSet<ZDOID> hashSet = new HashSet<ZDOID>();
			ZDOID controlledShipId = GetControlledShipId();
			foreach (ShipSnapshot ship in ships)
			{
				hashSet.Add(ship.Id);
				if (!((ZDOID)(ref controlledShipId)).IsNone() && ship.Id == controlledShipId)
				{
					RemoveTracked(ship.Id);
					continue;
				}
				if (!IsSanePosition(ship.Position))
				{
					RemoveTracked(ship.Id);
					continue;
				}
				string name = ShipCatalog.DisplayName(ship.Name, ship.TypeLabel);
				if (!Markers.TryGetValue(ship.Id, out MarkerState value))
				{
					value = new MarkerState();
					Markers[ship.Id] = value;
				}
				value.Pos = ship.Position;
				value.Name = name;
				value.PrefabHash = ship.PrefabHash;
			}
			List<ZDOID> list = new List<ZDOID>();
			foreach (ZDOID key in Markers.Keys)
			{
				if (!hashSet.Contains(key))
				{
					list.Add(key);
				}
			}
			foreach (ZDOID item in list)
			{
				RemoveTracked(item);
			}
			if (Markers.Count == _lastLoggedCount)
			{
				return;
			}
			_lastLoggedCount = Markers.Count;
			BoatTrackingPlugin.Log.LogInfo((object)$"Map markers tracked: {Markers.Count}");
			foreach (KeyValuePair<ZDOID, MarkerState> marker in Markers)
			{
				BoatTrackingPlugin.Log.LogInfo((object)$"  {marker.Value.Name} @ {marker.Value.Pos}");
			}
		}

		internal static void UpdateDrawnMarkers()
		{
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!BoatTrackingPlugin.ShowPins.Value || !Object.op_Implicit((Object)(object)Minimap.instance) || Markers.Count == 0)
				{
					if (Markers.Count == 0)
					{
						return;
					}
					{
						foreach (MarkerState value in Markers.Values)
						{
							DestroyMarker(value);
						}
						return;
					}
				}
				Minimap instance = Minimap.instance;
				GameObject field = GetField<GameObject>(instance, "m_largeRoot");
				RawImage field2 = GetField<RawImage>(instance, "m_mapImageLarge");
				RawImage field3 = GetField<RawImage>(instance, "m_mapImageSmall");
				RectTransform field4 = GetField<RectTransform>(instance, "m_pinRootLarge");
				RectTransform field5 = GetField<RectTransform>(instance, "m_pinRootSmall");
				GameObject field6 = GetField<GameObject>(instance, "m_pinPrefab");
				if (!Object.op_Implicit((Object)(object)field) || !Object.op_Implicit((Object)(object)field2) || !Object.op_Implicit((Object)(object)field3) || !Object.op_Implicit((Object)(object)field4) || !Object.op_Implicit((Object)(object)field5) || !Object.op_Implicit((Object)(object)field6))
				{
					return;
				}
				bool activeSelf = field.activeSelf;
				RawImage val = (activeSelf ? field2 : field3);
				RectTransform parent = (activeSelf ? field4 : field5);
				float size = (activeSelf ? GetField<float>(instance, "m_pinSizeLarge") : GetField<float>(instance, "m_pinSizeSmall"));
				foreach (MarkerState value2 in Markers.Values)
				{
					if (!IsPointVisible(value2.Pos, val, instance))
					{
						DestroyMarker(value2);
					}
					else
					{
						DrawMarker(value2, size, parent, val, field6, activeSelf, instance);
					}
				}
			}
			catch (Exception ex)
			{
				BoatTrackingPlugin.Log.LogWarning((object)("UpdateDrawnMarkers: " + ex.Message));
			}
		}

		private static void DrawMarker(MarkerState state, float size, RectTransform parent, RawImage rawImage, GameObject pinPrefab, bool large, Minimap mm)
		{
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: 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_00e3: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = state.Marker;
			if (!Object.op_Implicit((Object)(object)val) || (Object)(object)val.transform.parent != (Object)(object)parent)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					Object.Destroy((Object)(object)val);
				}
				val = (state.Marker = Object.Instantiate<GameObject>(pinPrefab));
				val.transform.SetParent((Transform)(object)parent, false);
				Image component = val.GetComponent<Image>();
				if (Object.op_Implicit((Object)(object)component))
				{
					component.sprite = ResolveSprite(state.PrefabHash) ?? component.sprite;
				}
				Transform transform = val.transform;
				RectTransform val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null);
				if (Object.op_Implicit((Object)(object)val2))
				{
					val2.SetSizeWithCurrentAnchors((Axis)0, size * 1.25f);
					val2.SetSizeWithCurrentAnchors((Axis)1, size * 1.25f);
				}
			}
			WorldToMapPoint(state.Pos, mm, out var mx, out var my);
			Vector2 anchoredPosition = MapPointToLocalGuiPos(mx, my, rawImage);
			Transform transform2 = val.transform;
			RectTransform val3 = (RectTransform)(object)((transform2 is RectTransform) ? transform2 : null);
			if (Object.op_Implicit((Object)(object)val3))
			{
				val3.anchoredPosition = anchoredPosition;
			}
			Transform val4 = val.transform.Find("Checked");
			if (Object.op_Implicit((Object)(object)val4))
			{
				((Component)val4).gameObject.SetActive(false);
			}
			SetMarkerName(val, state.Name, large, size);
		}

		private static void SetMarkerName(GameObject marker, string name, bool large, float pinSize)
		{
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: 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_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_020a: 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_0079: Expected O, but got Unknown
			Transform val = marker.transform.Find("Name");
			if (Object.op_Implicit((Object)(object)val))
			{
				((Component)val).gameObject.SetActive(false);
			}
			Transform val2 = marker.transform.Find("BoatTrackingName");
			Text val6;
			if (!Object.op_Implicit((Object)(object)val2))
			{
				if (!_loggedLabelOnce)
				{
					_loggedLabelOnce = true;
					StringBuilder stringBuilder = new StringBuilder("Pin prefab children:");
					foreach (Transform item in marker.transform)
					{
						Transform val3 = item;
						stringBuilder.Append(' ').Append(((Object)val3).name);
						Component[] components = ((Component)val3).GetComponents<Component>();
						foreach (Component val4 in components)
						{
							if (Object.op_Implicit((Object)(object)val4))
							{
								stringBuilder.Append('[').Append(((object)val4).GetType().Name).Append(']');
							}
						}
					}
					BoatTrackingPlugin.Log.LogInfo((object)stringBuilder.ToString());
				}
				GameObject val5 = new GameObject("BoatTrackingName");
				val2 = val5.transform;
				val2.SetParent(marker.transform, false);
				RectTransform obj = val5.AddComponent<RectTransform>();
				obj.sizeDelta = new Vector2(220f, 48f);
				obj.anchoredPosition = new Vector2(0f, (0f - pinSize) * 0.75f);
				((Transform)obj).localScale = Vector3.one;
				val6 = val5.AddComponent<Text>();
				val6.font = GetMapFont();
				val6.fontSize = Mathf.Clamp(Mathf.RoundToInt(pinSize * 0.35f), 10, 14);
				val6.fontStyle = (FontStyle)1;
				val6.alignment = (TextAnchor)1;
				((Graphic)val6).color = new Color(1f, 0.95f, 0.75f, 1f);
				val6.horizontalOverflow = (HorizontalWrapMode)1;
				val6.verticalOverflow = (VerticalWrapMode)1;
				((Graphic)val6).raycastTarget = false;
				val6.supportRichText = false;
				Outline obj2 = val5.AddComponent<Outline>();
				((Shadow)obj2).effectColor = new Color(0f, 0f, 0f, 0.9f);
				((Shadow)obj2).effectDistance = new Vector2(1.2f, -1.2f);
			}
			else
			{
				val6 = ((Component)val2).GetComponent<Text>();
				RectTransform val7 = (RectTransform)(object)((val2 is RectTransform) ? val2 : null);
				if (Object.op_Implicit((Object)(object)val7))
				{
					val7.anchoredPosition = new Vector2(0f, (0f - pinSize) * 0.75f);
				}
			}
			((Component)val2).gameObject.SetActive(large && !string.IsNullOrWhiteSpace(name));
			if (large && !((Object)(object)val6 == (Object)null))
			{
				val6.text = ((Localization.instance != null) ? Localization.instance.Localize(name) : name);
			}
		}

		private static Font GetMapFont()
		{
			if (Object.op_Implicit((Object)(object)_mapFont))
			{
				return _mapFont;
			}
			try
			{
				if (Object.op_Implicit((Object)(object)Minimap.instance))
				{
					Text[] componentsInChildren = ((Component)Minimap.instance).GetComponentsInChildren<Text>(true);
					foreach (Text val in componentsInChildren)
					{
						if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.font))
						{
							_mapFont = val.font;
							return _mapFont;
						}
					}
				}
			}
			catch
			{
			}
			_mapFont = Resources.GetBuiltinResource<Font>("Arial.ttf");
			if (!Object.op_Implicit((Object)(object)_mapFont))
			{
				_mapFont = Font.CreateDynamicFontFromOSFont("Arial", 16);
			}
			return _mapFont;
		}

		private static Sprite? ResolveSprite(int prefabHash)
		{
			if (SpriteCache.TryGetValue(prefabHash, out Sprite value))
			{
				return value ?? GetFallbackSprite();
			}
			Sprite val = null;
			try
			{
				if ((Object)(object)ZNetScene.instance != (Object)null)
				{
					GameObject prefab = ZNetScene.instance.GetPrefab(prefabHash);
					if (Object.op_Implicit((Object)(object)prefab))
					{
						Piece component = prefab.GetComponent<Piece>();
						if (Object.op_Implicit((Object)(object)component))
						{
							object? obj = AccessTools.Field(typeof(Piece), "m_icon")?.GetValue(component);
							val = (Sprite)((obj is Sprite) ? obj : null);
						}
					}
				}
			}
			catch
			{
			}
			SpriteCache[prefabHash] = val;
			return val ?? GetFallbackSprite();
		}

		private static Sprite? GetFallbackSprite()
		{
			if (Object.op_Implicit((Object)(object)_fallbackSprite))
			{
				return _fallbackSprite;
			}
			try
			{
				Minimap instance = Minimap.instance;
				if (!Object.op_Implicit((Object)(object)instance))
				{
					return null;
				}
				if (GetField<object>(instance, "m_icons") is IList list)
				{
					foreach (object item in list)
					{
						if (item == null)
						{
							continue;
						}
						string text = item.GetType().GetField("m_name")?.GetValue(item)?.ToString() ?? item.GetType().GetField("m_type")?.GetValue(item)?.ToString();
						object? obj = item.GetType().GetField("m_icon")?.GetValue(item);
						Sprite val = (Sprite)(((obj is Sprite) ? obj : null) ?? ((object)/*isinst with value type is only supported in some contexts*/));
						if (!((Object)(object)val == (Object)null))
						{
							if (text != null && (text.Contains("Death") || text.Contains("Icon0")))
							{
								_fallbackSprite = val;
								return _fallbackSprite;
							}
							if (_fallbackSprite == null)
							{
								_fallbackSprite = val;
							}
						}
					}
				}
			}
			catch
			{
			}
			return _fallbackSprite;
		}

		private static bool IsPointVisible(Vector3 p, RawImage map, Minimap mm)
		{
			//IL_0000: 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)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			WorldToMapPoint(p, mm, out var mx, out var my);
			Rect uvRect = map.uvRect;
			if (mx > ((Rect)(ref uvRect)).xMin && mx < ((Rect)(ref uvRect)).xMax && my > ((Rect)(ref uvRect)).yMin)
			{
				return my < ((Rect)(ref uvRect)).yMax;
			}
			return false;
		}

		private static void WorldToMapPoint(Vector3 p, Minimap mm, out float mx, out float my)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			int field = GetField<int>(mm, "m_textureSize");
			float field2 = GetField<float>(mm, "m_pixelSize");
			int num = field / 2;
			mx = p.x / field2 + (float)num;
			my = p.z / field2 + (float)num;
			mx /= field;
			my /= field;
		}

		private static Vector2 MapPointToLocalGuiPos(float mx, float my, RawImage img)
		{
			//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_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			Rect uvRect = img.uvRect;
			Vector2 result = default(Vector2);
			((Vector2)(ref result))..ctor((mx - ((Rect)(ref uvRect)).xMin) / ((Rect)(ref uvRect)).width, (my - ((Rect)(ref uvRect)).yMin) / ((Rect)(ref uvRect)).height);
			Rect rect = ((Graphic)img).rectTransform.rect;
			result.x *= ((Rect)(ref rect)).width;
			result.y *= ((Rect)(ref rect)).height;
			return result;
		}

		private static T? GetField<T>(object obj, string name)
		{
			FieldInfo fieldInfo = AccessTools.Field(obj.GetType(), name);
			if (fieldInfo == null)
			{
				return default(T);
			}
			object value = fieldInfo.GetValue(obj);
			if (value is T)
			{
				return (T)value;
			}
			if (typeof(T).IsValueType && value != null)
			{
				return (T)value;
			}
			return default(T);
		}

		private static void RemoveTracked(ZDOID id)
		{
			//IL_0005: 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)
			if (Markers.TryGetValue(id, out MarkerState value))
			{
				DestroyMarker(value);
				Markers.Remove(id);
			}
		}

		private static void DestroyMarker(MarkerState state)
		{
			if (Object.op_Implicit((Object)(object)state.Marker))
			{
				Object.Destroy((Object)(object)state.Marker);
				state.Marker = null;
			}
		}

		private static ZDOID GetControlledShipId()
		{
			//IL_000e: 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)
			//IL_0040: 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_005f: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer))
			{
				return ZDOID.None;
			}
			Ship controlledShip = localPlayer.GetControlledShip();
			if (!Object.op_Implicit((Object)(object)controlledShip))
			{
				return ZDOID.None;
			}
			ZNetView component = ((Component)controlledShip).GetComponent<ZNetView>();
			if (!Object.op_Implicit((Object)(object)component) || !component.IsValid())
			{
				return ZDOID.None;
			}
			ZDO zDO = component.GetZDO();
			if (zDO == null || !zDO.IsValid())
			{
				return ZDOID.None;
			}
			return zDO.m_uid;
		}

		private static bool IsSanePosition(Vector3 pos)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			if (float.IsNaN(pos.x) || float.IsNaN(pos.y) || float.IsNaN(pos.z))
			{
				return false;
			}
			if (float.IsInfinity(pos.x) || float.IsInfinity(pos.y) || float.IsInfinity(pos.z))
			{
				return false;
			}
			if (((Vector3)(ref pos)).sqrMagnitude < 0.01f)
			{
				return false;
			}
			return true;
		}
	}
	internal sealed class ShipSnapshot
	{
		public ZDOID Id;

		public Vector3 Position;

		public string Name = string.Empty;

		public string TypeLabel = string.Empty;

		public int PrefabHash;
	}
	internal static class ShipSync
	{
		private static float _timer;

		private static readonly List<ShipSnapshot> Latest = new List<ShipSnapshot>();

		private static readonly object Gate = new object();

		private static int _lastLoggedCount = -1;

		private static bool _loggedProtocolMismatch;

		internal static IReadOnlyList<ShipSnapshot> GetLatest()
		{
			lock (Gate)
			{
				return Latest.ToArray();
			}
		}

		internal static void Tick(float dt)
		{
			if ((Object)(object)ZNet.instance == (Object)null || ZDOMan.instance == null || !ZNet.instance.IsServer())
			{
				return;
			}
			float num = Mathf.Clamp(BoatTrackingPlugin.SyncInterval.Value, 1f, 30f);
			_timer += dt;
			if (_timer < num)
			{
				return;
			}
			_timer = 0f;
			try
			{
				BroadcastNow();
			}
			catch (Exception arg)
			{
				BoatTrackingPlugin.Log.LogError((object)$"Ship sync failed: {arg}");
			}
		}

		internal static void BroadcastNow()
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && ZDOMan.instance != null)
			{
				List<ShipSnapshot> list = CollectShips();
				ZPackage val = WritePackage(list);
				lock (Gate)
				{
					Latest.Clear();
					Latest.AddRange(list);
				}
				if (list.Count != _lastLoggedCount)
				{
					_lastLoggedCount = list.Count;
					BoatTrackingPlugin.Log.LogInfo((object)$"Broadcasting {list.Count} ship pin(s) to clients.");
				}
				if (!ZNet.instance.IsDedicated() && Object.op_Implicit((Object)(object)Minimap.instance))
				{
					ShipPins.Apply(list);
				}
				ZRoutedRpc.instance.InvokeRoutedRPC(0L, "BoatTracking.Sync", new object[1] { val });
			}
		}

		internal static void RequestFromServer()
		{
			if ((Object)(object)ZNet.instance == (Object)null || ZRoutedRpc.instance == null)
			{
				return;
			}
			if (ZNet.instance.IsServer())
			{
				BroadcastNow();
				return;
			}
			long serverPeerIdSafe = GetServerPeerIdSafe();
			if (serverPeerIdSafe != 0L)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(serverPeerIdSafe, "BoatTracking.RequestSync", Array.Empty<object>());
			}
		}

		internal static void HandleRequestSync(long sender)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				ZPackage val = WritePackage(CollectShips());
				ZRoutedRpc.instance.InvokeRoutedRPC(sender, "BoatTracking.Sync", new object[1] { val });
			}
		}

		internal static void HandleSync(long sender, ZPackage pkg)
		{
			if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsDedicated() || pkg == null || pkg.Size() == 0)
			{
				return;
			}
			try
			{
				pkg.SetPos(0);
				if (TryReadPackage(pkg, out List<ShipSnapshot> ships))
				{
					lock (Gate)
					{
						Latest.Clear();
						Latest.AddRange(ships);
					}
					if (ships.Count != _lastLoggedCount)
					{
						_lastLoggedCount = ships.Count;
						BoatTrackingPlugin.Log.LogInfo((object)$"Received {ships.Count} ship pin(s) from server.");
					}
					ShipPins.Apply(ships);
				}
			}
			catch (Exception arg)
			{
				BoatTrackingPlugin.Log.LogError((object)$"Ship sync receive failed: {arg}");
			}
		}

		internal static void HandleRename(long sender, ZPackage pkg)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && pkg != null)
			{
				pkg.SetPos(0);
				ZDOID id = pkg.ReadZDOID();
				string name = pkg.ReadString() ?? string.Empty;
				ApplyRename(id, name);
			}
		}

		internal static void SubmitRename(ZDOID id, string name)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			//IL_0031: 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)
			name = SanitizeName(name);
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return;
			}
			if (ZNet.instance.IsServer())
			{
				ApplyRename(id, name);
				return;
			}
			ZPackage val = new ZPackage();
			val.Write(id);
			val.Write(name);
			long serverPeerIdSafe = GetServerPeerIdSafe();
			if (serverPeerIdSafe == 0L)
			{
				BoatTrackingPlugin.Log.LogWarning((object)"Rename skipped - no server peer id yet.");
				return;
			}
			ZRoutedRpc.instance.InvokeRoutedRPC(serverPeerIdSafe, "BoatTracking.Rename", new object[1] { val });
		}

		private static long GetServerPeerIdSafe()
		{
			try
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(ZRoutedRpc), "GetServerPeerID", (Type[])null, (Type[])null);
				if (methodInfo != null && ZRoutedRpc.instance != null && methodInfo.Invoke(ZRoutedRpc.instance, null) is long result)
				{
					return result;
				}
			}
			catch (Exception ex)
			{
				BoatTrackingPlugin.Log.LogWarning((object)("GetServerPeerID reflection failed: " + ex.Message));
			}
			try
			{
				ZNetPeer val = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetServerPeer() : null);
				if (val != null)
				{
					FieldInfo fieldInfo = AccessTools.Field(((object)val).GetType(), "m_uid");
					if (fieldInfo != null && fieldInfo.GetValue(val) is long result2)
					{
						return result2;
					}
				}
			}
			catch (Exception ex2)
			{
				BoatTrackingPlugin.Log.LogWarning((object)("GetServerPeer fallback failed: " + ex2.Message));
			}
			return 0L;
		}

		private static void ApplyRename(ZDOID id, string name)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			ZDOMan instance = ZDOMan.instance;
			ZDO val = ((instance != null) ? instance.GetZDO(id) : null);
			if (val != null && val.IsValid() && ShipCatalog.TryGet(val.GetPrefab(), out var _))
			{
				val.SetOwner(ZDOMan.GetSessionID());
				val.Set("BoatTracking.Name", name);
				BroadcastNow();
			}
		}

		internal static string SanitizeName(string? name)
		{
			if (name == null || string.IsNullOrWhiteSpace(name))
			{
				return string.Empty;
			}
			string text = name.Trim();
			int num = Mathf.Clamp(BoatTrackingPlugin.MaxNameLength.Value, 1, 40);
			if (text.Length > num)
			{
				text = text.Substring(0, num);
			}
			return text;
		}

		private static List<ShipSnapshot> CollectShips()
		{
			List<ShipSnapshot> result = new List<ShipSnapshot>();
			ZDOMan instance = ZDOMan.instance;
			if (instance == null)
			{
				return result;
			}
			HashSet<ZDOID> seen = new HashSet<ZDOID>();
			CollectFromObjectsById(instance, Consider);
			if (result.Count == 0)
			{
				CollectFromPrefabIterative(instance, Consider);
			}
			return result;
			void Consider(ZDO? zdo)
			{
				//IL_0013: 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_005c: Unknown result type (might be due to invalid IL or missing references)
				//IL_006a: Unknown result type (might be due to invalid IL or missing references)
				//IL_006f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0075: Unknown result type (might be due to invalid IL or missing references)
				//IL_0076: Unknown result type (might be due to invalid IL or missing references)
				if (zdo != null && zdo.IsValid() && seen.Add(zdo.m_uid) && ShipCatalog.TryGet(zdo.GetPrefab(), out var info))
				{
					string text = zdo.GetString("BoatTracking.Name", string.Empty);
					if (BoatTrackingPlugin.ShowUnnamed.Value || !string.IsNullOrWhiteSpace(text))
					{
						Vector3 position = zdo.GetPosition();
						result.Add(new ShipSnapshot
						{
							Id = zdo.m_uid,
							Position = position,
							Name = (text?.Trim() ?? string.Empty),
							TypeLabel = info.Label,
							PrefabHash = info.Hash
						});
					}
				}
			}
		}

		private static void CollectFromObjectsById(ZDOMan zdoMan, Action<ZDO?> consider)
		{
			try
			{
				object obj = AccessTools.Field(typeof(ZDOMan), "m_objectsByID")?.GetValue(zdoMan);
				if (obj is IDictionary dictionary)
				{
					{
						foreach (object value2 in dictionary.Values)
						{
							ZDO val = (ZDO)((value2 is ZDO) ? value2 : null);
							if (val != null)
							{
								consider(val);
							}
						}
						return;
					}
				}
				if (!(obj is IEnumerable enumerable))
				{
					return;
				}
				foreach (object item in enumerable)
				{
					ZDO val2 = (ZDO)((item is ZDO) ? item : null);
					if (val2 != null)
					{
						consider(val2);
					}
					else if (item is DictionaryEntry dictionaryEntry)
					{
						object? value = dictionaryEntry.Value;
						ZDO val3 = (ZDO)((value is ZDO) ? value : null);
						if (val3 != null)
						{
							consider(val3);
						}
					}
				}
			}
			catch (Exception ex)
			{
				BoatTrackingPlugin.Log.LogWarning((object)("ZDO id scan failed: " + ex.Message));
			}
		}

		private static void CollectFromPrefabIterative(ZDOMan zdoMan, Action<ZDO?> consider)
		{
			List<ZDO> list = new List<ZDO>();
			ShipCatalog.PrefabInfo[] prefabs = ShipCatalog.Prefabs;
			for (int i = 0; i < prefabs.Length; i++)
			{
				ShipCatalog.PrefabInfo prefabInfo = prefabs[i];
				list.Clear();
				int num = 0;
				int num2 = 0;
				while (!zdoMan.GetAllZDOsWithPrefabIterative(prefabInfo.Prefab, list, ref num))
				{
					if (++num2 > 100000)
					{
						BoatTrackingPlugin.Log.LogWarning((object)("Prefab scan aborted for " + prefabInfo.Prefab));
						break;
					}
				}
				foreach (ZDO item in list)
				{
					consider(item);
				}
			}
		}

		private static ZPackage WritePackage(List<ShipSnapshot> ships)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			ZPackage val = new ZPackage();
			val.Write(1);
			val.Write(ships.Count);
			foreach (ShipSnapshot ship in ships)
			{
				val.Write(ship.Id);
				val.Write(ship.Position);
				val.Write(ship.Name ?? string.Empty);
				val.Write(ship.TypeLabel ?? string.Empty);
				val.Write(ship.PrefabHash);
			}
			return val;
		}

		private static bool TryReadPackage(ZPackage pkg, out List<ShipSnapshot> ships)
		{
			//IL_005e: 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_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			ships = new List<ShipSnapshot>();
			int num = pkg.ReadInt();
			if (num != 1)
			{
				NotifyProtocolMismatch(num);
				return false;
			}
			int num2 = pkg.ReadInt();
			if (num2 < 0 || num2 > 512)
			{
				BoatTrackingPlugin.Log.LogWarning((object)$"Ship sync rejected: unreasonable ship count {num2}.");
				return false;
			}
			ships = new List<ShipSnapshot>(num2);
			for (int i = 0; i < num2; i++)
			{
				ships.Add(new ShipSnapshot
				{
					Id = pkg.ReadZDOID(),
					Position = pkg.ReadVector3(),
					Name = (pkg.ReadString() ?? string.Empty),
					TypeLabel = (pkg.ReadString() ?? string.Empty),
					PrefabHash = pkg.ReadInt()
				});
			}
			return true;
		}

		private static void NotifyProtocolMismatch(int serverVersion)
		{
			if (_loggedProtocolMismatch)
			{
				return;
			}
			_loggedProtocolMismatch = true;
			int num = 1;
			string text = ((serverVersion >= num) ? $"BoatTracking: your mod is outdated (server protocol {serverVersion}, you have {num}). Update BoatTracking to match the server." : $"BoatTracking: server mod is outdated (server protocol {serverVersion}, you have {num}). Ask the host to update BoatTracking.");
			BoatTrackingPlugin.Log.LogWarning((object)text);
			try
			{
				if (Object.op_Implicit((Object)(object)MessageHud.instance))
				{
					MessageHud.instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false, true);
				}
			}
			catch (Exception ex)
			{
				BoatTrackingPlugin.Log.LogWarning((object)("Could not show protocol mismatch HUD: " + ex.Message));
			}
			try
			{
				if (Object.op_Implicit((Object)(object)Chat.instance))
				{
					((Terminal)Chat.instance).AddString("BoatTracking", text, (Type)1, false);
				}
			}
			catch
			{
			}
		}
	}
}
namespace BoatTracking.Patches
{
	[HarmonyPatch(typeof(Player), "Interact")]
	internal static class InteractRenamePatch
	{
		private sealed class ShipNameReceiver : TextReceiver
		{
			private readonly ZDOID _id;

			public ShipNameReceiver(ZDOID id)
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				_id = id;
			}

			public string GetText()
			{
				//IL_000d: Unknown result type (might be due to invalid IL or missing references)
				ZDOMan instance = ZDOMan.instance;
				ZDO val = ((instance != null) ? instance.GetZDO(_id) : null);
				if (val == null || !val.IsValid())
				{
					return string.Empty;
				}
				return val.GetString("BoatTracking.Name", string.Empty);
			}

			public void SetText(string text)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				ShipSync.SubmitRename(_id, text);
			}
		}

		private const float RenameHoldSeconds = 0.75f;

		internal static bool OpenedThisHold;

		private static float UsePressStartedAt = -1f;

		private static bool SuppressTypingUntilUseReleased;

		private static string SeedText = string.Empty;

		private static bool Prefix(Player __instance, GameObject go, bool hold, bool alt)
		{
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			if (ShouldLeaveUseAlone(__instance, go))
			{
				OpenedThisHold = false;
				return true;
			}
			if (!hold)
			{
				return true;
			}
			if (OpenedThisHold || TextInput.IsVisible())
			{
				if (IsRenameTarget(go))
				{
					return false;
				}
				return true;
			}
			if (!IsRenameTarget(go))
			{
				return true;
			}
			if (UsePressStartedAt < 0f || Time.time - UsePressStartedAt < 0.75f)
			{
				return true;
			}
			Ship val = ShipCatalog.FindShipFrom(go.GetComponent<Component>());
			if (!Object.op_Implicit((Object)(object)val))
			{
				return true;
			}
			ZNetView component = ((Component)val).GetComponent<ZNetView>();
			if (!Object.op_Implicit((Object)(object)component) || !component.IsValid())
			{
				return true;
			}
			if (!Object.op_Implicit((Object)(object)TextInput.instance))
			{
				return true;
			}
			int num = Mathf.Clamp(BoatTrackingPlugin.MaxNameLength.Value, 1, 40);
			ShipNameReceiver shipNameReceiver = new ShipNameReceiver(component.GetZDO().m_uid);
			SeedText = shipNameReceiver.GetText() ?? string.Empty;
			SuppressTypingUntilUseReleased = true;
			OpenedThisHold = true;
			TextInput.instance.RequestText((TextReceiver)(object)shipNameReceiver, "Rename ship", num);
			ForceTextInputText(SeedText);
			return false;
		}

		internal static void OnPlayerUpdate(Player player)
		{
			if ((Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			if (ZInput.GetButton("Use") || ZInput.GetButton("JoyUse"))
			{
				if (UsePressStartedAt < 0f)
				{
					UsePressStartedAt = Time.time;
				}
				if (SuppressTypingUntilUseReleased && TextInput.IsVisible())
				{
					ForceTextInputText(SeedText);
				}
				return;
			}
			UsePressStartedAt = -1f;
			OpenedThisHold = false;
			if (SuppressTypingUntilUseReleased)
			{
				SuppressTypingUntilUseReleased = false;
				if (TextInput.IsVisible())
				{
					ForceTextInputText(SeedText);
				}
			}
		}

		private static void ForceTextInputText(string text)
		{
			TextInput instance = TextInput.instance;
			if (!Object.op_Implicit((Object)(object)instance))
			{
				return;
			}
			string[] array = new string[3] { "m_textField", "m_inputField", "m_input" };
			foreach (string text2 in array)
			{
				FieldInfo fieldInfo = AccessTools.Field(typeof(TextInput), text2);
				if (fieldInfo == null)
				{
					continue;
				}
				object value = fieldInfo.GetValue(instance);
				if (value == null)
				{
					continue;
				}
				if (value is string)
				{
					fieldInfo.SetValue(instance, text);
					continue;
				}
				PropertyInfo property = value.GetType().GetProperty("text", BindingFlags.Instance | BindingFlags.Public);
				if (property != null && property.CanWrite)
				{
					property.SetValue(value, text, null);
					break;
				}
			}
		}

		internal static bool ShouldLeaveUseAlone(Player player, GameObject? go)
		{
			if (!Object.op_Implicit((Object)(object)player))
			{
				return false;
			}
			if (IsRudderHover(go))
			{
				return true;
			}
			if (Object.op_Implicit((Object)(object)go) && Object.op_Implicit((Object)(object)go.GetComponentInParent<Ladder>()))
			{
				return true;
			}
			if (IsLocalPlayerSteering(player))
			{
				return true;
			}
			return false;
		}

		internal static bool IsLocalPlayerSteering(Player player)
		{
			if (!Object.op_Implicit((Object)(object)player))
			{
				return false;
			}
			if (Object.op_Implicit((Object)(object)player.GetControlledShip()))
			{
				return true;
			}
			IDoodadController doodadController = player.GetDoodadController();
			if (doodadController != null && doodadController.IsValid())
			{
				return doodadController.GetControlledComponent() is Ship;
			}
			return false;
		}

		internal static bool IsRudderHover(GameObject? go)
		{
			if (Object.op_Implicit((Object)(object)go))
			{
				return Object.op_Implicit((Object)(object)go.GetComponentInParent<ShipControlls>());
			}
			return false;
		}

		private static bool IsRenameTarget(GameObject? go)
		{
			if (!Object.op_Implicit((Object)(object)go))
			{
				return false;
			}
			if (Object.op_Implicit((Object)(object)go.GetComponentInParent<Container>()))
			{
				return false;
			}
			if (Object.op_Implicit((Object)(object)go.GetComponentInParent<Ladder>()))
			{
				return false;
			}
			if (Object.op_Implicit((Object)(object)go.GetComponentInParent<ShipControlls>()))
			{
				return false;
			}
			return Object.op_Implicit((Object)(object)ShipCatalog.FindShipFrom(go.GetComponent<Component>()));
		}
	}
	[HarmonyPatch(typeof(Player), "Update")]
	internal static class InteractRenameHoldTrackPatch
	{
		private static void Postfix(Player __instance)
		{
			InteractRenamePatch.OnPlayerUpdate(__instance);
		}
	}
	[HarmonyPatch(typeof(ShipControlls), "GetHoverText")]
	internal static class ShipControllsHoverPatch
	{
		private static void Postfix(ShipControlls __instance, ref string __result)
		{
			HoverText.AppendShipName((Component)(object)__instance, ref __result, showRenameHint: false);
		}
	}
	[HarmonyPatch(typeof(Chair), "GetHoverText")]
	internal static class ChairHoverPatch
	{
		private static void Postfix(Chair __instance, ref string __result)
		{
			if (Object.op_Implicit((Object)(object)ShipCatalog.FindShipFrom((Component?)(object)__instance)))
			{
				bool showRenameHint = Object.op_Implicit((Object)(object)Player.m_localPlayer) && !InteractRenamePatch.ShouldLeaveUseAlone(Player.m_localPlayer, ((Component)__instance).gameObject);
				HoverText.AppendShipName((Component)(object)__instance, ref __result, showRenameHint);
			}
		}
	}
	internal static class HoverText
	{
		internal static void AppendShipName(Component component, ref string result, bool showRenameHint)
		{
			Ship val = ShipCatalog.FindShipFrom(component);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return;
			}
			ZNetView component2 = ((Component)val).GetComponent<ZNetView>();
			if (!Object.op_Implicit((Object)(object)component2) || !component2.IsValid())
			{
				return;
			}
			ZDO zDO = component2.GetZDO();
			if (ShipCatalog.TryGet(zDO.GetPrefab(), out var info))
			{
				string text = ShipCatalog.DisplayName(zDO, info);
				string text2 = string.Empty;
				if (showRenameHint)
				{
					text2 = Localization.instance.Localize("\n[<color=yellow><b>Hold $KEY_Use</b></color>] Rename ship");
				}
				if (string.IsNullOrEmpty(result))
				{
					result = "<color=orange>" + text + "</color>" + text2;
				}
				else if (!result.Contains(text))
				{
					result = "<color=orange>" + text + "</color>\n" + result + text2;
				}
				else if (text2.Length > 0 && !result.Contains("Rename ship"))
				{
					result += text2;
				}
			}
		}
	}
	[HarmonyPatch(typeof(Game), "Start")]
	internal static class GameStartPatch
	{
		private static void Prefix()
		{
			ZRoutedRpc.instance.Register("BoatTracking.RequestSync", (Action<long>)ShipSync.HandleRequestSync);
			ZRoutedRpc.instance.Register<ZPackage>("BoatTracking.Sync", (Action<long, ZPackage>)ShipSync.HandleSync);
			ZRoutedRpc.instance.Register<ZPackage>("BoatTracking.Rename", (Action<long, ZPackage>)ShipSync.HandleRename);
		}
	}
	[HarmonyPatch(typeof(Game), "Logout")]
	internal static class GameLogoutPatch
	{
		private static void Prefix()
		{
			ShipPins.Clear();
		}
	}
	[HarmonyPatch(typeof(Minimap), "OnDestroy")]
	internal static class MinimapDestroyPatch
	{
		private static void Postfix()
		{
			ShipPins.Clear();
		}
	}
	[HarmonyPatch(typeof(Minimap), "Awake")]
	internal static class MinimapAwakePatch
	{
		private static void Postfix()
		{
			ShipSync.RequestFromServer();
		}
	}
	[HarmonyPatch(typeof(Minimap), "UpdatePins")]
	internal static class MinimapUpdatePinsPatch
	{
		private static void Postfix()
		{
			ShipPins.UpdateDrawnMarkers();
		}
	}
	[HarmonyPatch(typeof(Minimap), "UpdateMap")]
	internal static class MinimapUpdateRefreshPatch
	{
		private static float _next;

		private static void Postfix()
		{
			if (!(Time.time < _next))
			{
				_next = Time.time + 5f;
				IReadOnlyList<ShipSnapshot> latest = ShipSync.GetLatest();
				if (latest.Count > 0)
				{
					ShipPins.Apply(latest);
				}
			}
		}
	}
}