Decompiled source of CartTracking v1.0.0

BepInEx/plugins/CartTracking.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using 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("CartTracking")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+a4fb8dce80820c0961c57266fa24d5d796d707ac")]
[assembly: AssemblyProduct("CartTracking")]
[assembly: AssemblyTitle("CartTracking")]
[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 CartTracking
{
	internal static class CartCatalog
	{
		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[1]
		{
			new PrefabInfo("Cart", "Cart")
		};

		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 class CartPins
	{
		private sealed class MarkerState
		{
			public Vector3 Pos;

			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;

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

		internal static void Apply(IReadOnlyList<CartSnapshot> carts)
		{
			try
			{
				ApplyInternal(carts);
			}
			catch (Exception arg)
			{
				CartTrackingPlugin.Log.LogError((object)$"CartPins.Apply failed: {arg}");
			}
		}

		private static void ApplyInternal(IReadOnlyList<CartSnapshot> carts)
		{
			//IL_002a: 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_0055: 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_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: 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_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_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			if (!CartTrackingPlugin.ShowPins.Value)
			{
				Clear();
				return;
			}
			HashSet<ZDOID> hashSet = new HashSet<ZDOID>();
			foreach (CartSnapshot cart in carts)
			{
				hashSet.Add(cart.Id);
				if (!IsSanePosition(cart.Position))
				{
					RemoveTracked(cart.Id);
					continue;
				}
				if (!Markers.TryGetValue(cart.Id, out MarkerState value))
				{
					value = new MarkerState();
					Markers[cart.Id] = value;
				}
				value.Pos = cart.Position;
				value.PrefabHash = cart.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;
			CartTrackingPlugin.Log.LogInfo((object)$"Map markers tracked: {Markers.Count}");
			foreach (KeyValuePair<ZDOID, MarkerState> marker in Markers)
			{
				CartTrackingPlugin.Log.LogInfo((object)$"  Cart @ {marker.Value.Pos}");
			}
		}

		internal static void UpdateDrawnMarkers()
		{
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!CartTrackingPlugin.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 val2 = (activeSelf ? field4 : field5);
				if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)val2) || !Object.op_Implicit((Object)(object)((Graphic)val).rectTransform))
				{
					return;
				}
				float num = (activeSelf ? GetField<float>(instance, "m_pinSizeLarge") : GetField<float>(instance, "m_pinSizeSmall"));
				if (num <= 0f)
				{
					num = (activeSelf ? 32f : 16f);
				}
				foreach (MarkerState item in new List<MarkerState>(Markers.Values))
				{
					if (item != null)
					{
						if (!IsPointVisible(item.Pos, val, instance))
						{
							DestroyMarker(item);
						}
						else
						{
							DrawMarker(item, num, val2, val, field6, instance);
						}
					}
				}
			}
			catch (Exception ex)
			{
				CartTrackingPlugin.Log.LogWarning((object)("UpdateDrawnMarkers: " + ex.Message));
			}
		}

		private static void DrawMarker(MarkerState state, float size, RectTransform parent, RawImage rawImage, GameObject pinPrefab, 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);
			}
			Transform val5 = val.transform.Find("Name");
			if (Object.op_Implicit((Object)(object)val5))
			{
				((Component)val5).gameObject.SetActive(false);
			}
		}

		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_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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");
			if (field <= 0 || field2 <= 0f)
			{
				mx = 0f;
				my = 0f;
				return;
			}
			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_0015: 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_0021: 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_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)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)img) || !Object.op_Implicit((Object)(object)((Graphic)img).rectTransform))
			{
				return Vector2.zero;
			}
			Rect uvRect = img.uvRect;
			if (((Rect)(ref uvRect)).width <= 0f || ((Rect)(ref uvRect)).height <= 0f)
			{
				return Vector2.zero;
			}
			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 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 CartSnapshot
	{
		public ZDOID Id;

		public Vector3 Position;

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

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

		private static readonly object Gate = new object();

		private static int _lastLoggedCount = -1;

		private static bool _loggedProtocolMismatch;

		internal static IReadOnlyList<CartSnapshot> 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(CartTrackingPlugin.SyncInterval.Value, 1f, 30f);
			_timer += dt;
			if (_timer < num)
			{
				return;
			}
			_timer = 0f;
			try
			{
				BroadcastNow();
			}
			catch (Exception arg)
			{
				CartTrackingPlugin.Log.LogError((object)$"Cart sync failed: {arg}");
			}
		}

		internal static void BroadcastNow()
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && ZDOMan.instance != null)
			{
				List<CartSnapshot> list = CollectCarts();
				ZPackage val = WritePackage(list);
				lock (Gate)
				{
					Latest.Clear();
					Latest.AddRange(list);
				}
				if (list.Count != _lastLoggedCount)
				{
					_lastLoggedCount = list.Count;
					CartTrackingPlugin.Log.LogInfo((object)$"Broadcasting {list.Count} cart pin(s) to clients.");
				}
				if (!ZNet.instance.IsDedicated() && Object.op_Implicit((Object)(object)Minimap.instance))
				{
					CartPins.Apply(list);
				}
				ZRoutedRpc.instance.InvokeRoutedRPC(0L, "CartTracking.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, "CartTracking.RequestSync", Array.Empty<object>());
			}
		}

		internal static void HandleRequestSync(long sender)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				ZPackage val = WritePackage(CollectCarts());
				ZRoutedRpc.instance.InvokeRoutedRPC(sender, "CartTracking.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<CartSnapshot> carts))
				{
					lock (Gate)
					{
						Latest.Clear();
						Latest.AddRange(carts);
					}
					if (carts.Count != _lastLoggedCount)
					{
						_lastLoggedCount = carts.Count;
						CartTrackingPlugin.Log.LogInfo((object)$"Received {carts.Count} cart pin(s) from server.");
					}
					CartPins.Apply(carts);
				}
			}
			catch (Exception arg)
			{
				CartTrackingPlugin.Log.LogError((object)$"Cart sync receive failed: {arg}");
			}
		}

		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)
			{
				CartTrackingPlugin.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)
			{
				CartTrackingPlugin.Log.LogWarning((object)("GetServerPeer fallback failed: " + ex2.Message));
			}
			return 0L;
		}

		private static List<CartSnapshot> CollectCarts()
		{
			List<CartSnapshot> result = new List<CartSnapshot>();
			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_0031: 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_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_004f: 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)
				if (zdo != null && zdo.IsValid() && seen.Add(zdo.m_uid) && CartCatalog.TryGet(zdo.GetPrefab(), out var info))
				{
					Vector3 position = zdo.GetPosition();
					result.Add(new CartSnapshot
					{
						Id = zdo.m_uid,
						Position = position,
						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)
			{
				CartTrackingPlugin.Log.LogWarning((object)("ZDO id scan failed: " + ex.Message));
			}
		}

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

		private static ZPackage WritePackage(List<CartSnapshot> carts)
		{
			//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(carts.Count);
			foreach (CartSnapshot cart in carts)
			{
				val.Write(cart.Id);
				val.Write(cart.Position);
				val.Write(cart.PrefabHash);
			}
			return val;
		}

		private static bool TryReadPackage(ZPackage pkg, out List<CartSnapshot> carts)
		{
			//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)
			carts = new List<CartSnapshot>();
			int num = pkg.ReadInt();
			if (num != 1)
			{
				NotifyProtocolMismatch(num);
				return false;
			}
			int num2 = pkg.ReadInt();
			if (num2 < 0 || num2 > 512)
			{
				CartTrackingPlugin.Log.LogWarning((object)$"Cart sync rejected: unreasonable cart count {num2}.");
				return false;
			}
			carts = new List<CartSnapshot>(num2);
			for (int i = 0; i < num2; i++)
			{
				carts.Add(new CartSnapshot
				{
					Id = pkg.ReadZDOID(),
					Position = pkg.ReadVector3(),
					PrefabHash = pkg.ReadInt()
				});
			}
			return true;
		}

		private static void NotifyProtocolMismatch(int serverVersion)
		{
			if (_loggedProtocolMismatch)
			{
				return;
			}
			_loggedProtocolMismatch = true;
			int num = 1;
			string text = ((serverVersion >= num) ? $"CartTracking: your mod is outdated (server protocol {serverVersion}, you have {num}). Update CartTracking to match the server." : $"CartTracking: server mod is outdated (server protocol {serverVersion}, you have {num}). Ask the host to update CartTracking.");
			CartTrackingPlugin.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)
			{
				CartTrackingPlugin.Log.LogWarning((object)("Could not show protocol mismatch HUD: " + ex.Message));
			}
			try
			{
				if (Object.op_Implicit((Object)(object)Chat.instance))
				{
					((Terminal)Chat.instance).AddString("CartTracking", text, (Type)1, false);
				}
			}
			catch
			{
			}
		}
	}
	[BepInPlugin("matthb1.carttracking", "CartTracking", "1.0.0")]
	public class CartTrackingPlugin : BaseUnityPlugin
	{
		public const string GUID = "matthb1.carttracking";

		public const string NAME = "CartTracking";

		public const string VERSION = "1.0.0";

		public const int ProtocolVersion = 1;

		public const string RpcSync = "CartTracking.Sync";

		public const string RpcRequestSync = "CartTracking.RequestSync";

		internal static CartTrackingPlugin Instance;

		internal static ConfigEntry<bool> ShowPins;

		internal static ConfigEntry<float> SyncInterval;

		internal static ManualLogSource Log;

		private void Awake()
		{
			//IL_006a: 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 cart pins on the minimap and world map.");
			SyncInterval = ((BaseUnityPlugin)this).Config.Bind<float>("General", "SyncInterval", 3f, "Seconds between server cart-position syncs (1-30).");
			((BaseUnityPlugin)this).Logger.LogInfo((object)"CartTracking 1.0.0 loaded - server syncs cart map pins.");
			new Harmony("matthb1.carttracking").PatchAll();
		}

		private void Update()
		{
			CartSync.Tick(Time.deltaTime);
		}
	}
}
namespace CartTracking.Patches
{
	[HarmonyPatch(typeof(Game), "Start")]
	internal static class GameStartPatch
	{
		private static void Prefix()
		{
			ZRoutedRpc.instance.Register("CartTracking.RequestSync", (Action<long>)CartSync.HandleRequestSync);
			ZRoutedRpc.instance.Register<ZPackage>("CartTracking.Sync", (Action<long, ZPackage>)CartSync.HandleSync);
		}
	}
	[HarmonyPatch(typeof(Game), "Logout")]
	internal static class GameLogoutPatch
	{
		private static void Prefix()
		{
			CartPins.Clear();
		}
	}
	[HarmonyPatch(typeof(Minimap), "OnDestroy")]
	internal static class MinimapDestroyPatch
	{
		private static void Postfix()
		{
			CartPins.Clear();
		}
	}
	[HarmonyPatch(typeof(Minimap), "Awake")]
	internal static class MinimapAwakePatch
	{
		private static void Postfix()
		{
			CartSync.RequestFromServer();
		}
	}
	[HarmonyPatch(typeof(Minimap), "UpdatePins")]
	internal static class MinimapUpdatePinsPatch
	{
		private static void Postfix()
		{
			CartPins.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<CartSnapshot> latest = CartSync.GetLatest();
				if (latest.Count > 0)
				{
					CartPins.Apply(latest);
				}
			}
		}
	}
}