Decompiled source of SharedMap v2.1.0

plugins/TXC.SharedMap.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
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 Splatform;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: AssemblyCompany("TXC.SharedMap")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("2.1.0.0")]
[assembly: AssemblyInformationalVersion("2.1.0")]
[assembly: AssemblyProduct("TXC.SharedMap")]
[assembly: AssemblyTitle("TXC.SharedMap")]
[assembly: AssemblyVersion("2.1.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[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 TXC.SharedMap
{
	[BepInPlugin("txc.sharedmap", "TXC Shared Map", "2.1.0")]
	public class Plugin : BaseUnityPlugin
	{
		private static class Patches
		{
			[HarmonyPostfix]
			[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
			private static void RegisterRpcs(ZNet __instance, ZNetPeer peer)
			{
				string[] array;
				if (__instance.IsServer())
				{
					array = new string[3] { "TXCSharedMap_Hello", "TXCSharedMap_ClientExplored", "TXCSharedMap_ClientPins" };
					foreach (string method in array)
					{
						peer.m_rpc.Register<ZPackage>(method, (Action<ZRpc, ZPackage>)delegate(ZRpc rpc, ZPackage pkg)
						{
							Server?.Handle(peer.m_uid, method, pkg);
						});
					}
					return;
				}
				array = new string[6] { "TXCSharedMap_Welcome", "TXCSharedMap_Band", "TXCSharedMap_PinList", "TXCSharedMap_SyncDone", "TXCSharedMap_ServerExplored", "TXCSharedMap_ServerPins" };
				foreach (string method2 in array)
				{
					peer.m_rpc.Register<ZPackage>(method2, (Action<ZRpc, ZPackage>)delegate(ZRpc rpc, ZPackage pkg)
					{
						Client?.Handle(method2, pkg);
					});
				}
			}

			[HarmonyPostfix]
			[HarmonyPatch(typeof(ZNet), "RPC_ServerSyncedPlayerData")]
			private static void ForcePublicRefPos(ZNet __instance, ZRpc rpc)
			{
				if (!__instance.IsServer() || !ForcePublicPosition.Value)
				{
					return;
				}
				foreach (ZNetPeer peer in __instance.GetPeers())
				{
					if (peer.m_rpc == rpc)
					{
						peer.m_publicRefPos = true;
					}
				}
			}

			[HarmonyPostfix]
			[HarmonyPatch(typeof(Minimap), "ShowPinNameInput")]
			private static void PinPlaced(Minimap __instance)
			{
				if ((Object)(object)Client?.Minimap == (Object)(object)__instance)
				{
					Client.OnPinPlaced(SharedMapClient.NamePin(__instance));
				}
			}

			[HarmonyPrefix]
			[HarmonyPatch(typeof(Minimap), "RemovePin", new Type[] { typeof(PinData) })]
			private static bool RemovePin(Minimap __instance, PinData pin)
			{
				if (!((Object)(object)Client?.Minimap != (Object)(object)__instance))
				{
					return Client.AllowRemove(pin);
				}
				return true;
			}

			[HarmonyPrefix]
			[HarmonyPatch(typeof(Minimap), "RemovePin", new Type[]
			{
				typeof(Vector3),
				typeof(float)
			})]
			private static void UserRemoveStart(Minimap __instance)
			{
				if ((Object)(object)Client?.Minimap == (Object)(object)__instance)
				{
					Client.UserRemoving = true;
				}
			}

			[HarmonyFinalizer]
			[HarmonyPatch(typeof(Minimap), "RemovePin", new Type[]
			{
				typeof(Vector3),
				typeof(float)
			})]
			private static void UserRemoveEnd()
			{
				if (Client != null)
				{
					Client.UserRemoving = false;
				}
			}

			[HarmonyPrefix]
			[HarmonyPatch(typeof(Minimap), "GetMapData")]
			private static void ProfileSaveStart(Minimap __instance, out List<PinData> __state)
			{
				__state = (((Object)(object)Client?.Minimap == (Object)(object)__instance) ? Client.BeginExcludePublic() : null);
			}

			[HarmonyFinalizer]
			[HarmonyPatch(typeof(Minimap), "GetMapData")]
			private static void ProfileSaveEnd(List<PinData> __state)
			{
				SharedMapClient.EndExcludePublic(__state);
			}

			[HarmonyPrefix]
			[HarmonyPatch(typeof(Minimap), "GetSharedMapData")]
			private static void TableWriteStart(Minimap __instance, out List<PinData> __state)
			{
				__state = (((Object)(object)Client?.Minimap == (Object)(object)__instance) ? Client.BeginExcludePublic() : null);
			}

			[HarmonyFinalizer]
			[HarmonyPatch(typeof(Minimap), "GetSharedMapData")]
			private static void TableWriteEnd(List<PinData> __state)
			{
				SharedMapClient.EndExcludePublic(__state);
			}

			[HarmonyPostfix]
			[HarmonyPatch(typeof(Minimap), "ResetSharedMapData")]
			private static void SharedMapDataReset(Minimap __instance)
			{
				if ((Object)(object)Client?.Minimap == (Object)(object)__instance)
				{
					Client.OnSharedMapDataReset();
				}
			}

			[HarmonyPrefix]
			[HarmonyPatch(typeof(Minimap), "OnMapLeftClick")]
			private static bool MapLeftClick(Minimap __instance)
			{
				if (!((Object)(object)Client?.Minimap != (Object)(object)__instance))
				{
					return Client.OnMapLeftClick();
				}
				return true;
			}

			[HarmonyPostfix]
			[HarmonyPatch(typeof(Minimap), "Update")]
			private static void MinimapUpdate(Minimap __instance)
			{
				if ((Object)(object)Client?.Minimap == (Object)(object)__instance)
				{
					Client.ApplySharedFade();
				}
			}

			[HarmonyPostfix]
			[HarmonyPatch(typeof(Minimap), "UpdatePins")]
			private static void TintPins(Minimap __instance)
			{
				if ((Object)(object)Client?.Minimap == (Object)(object)__instance)
				{
					Client.TintPublicPins();
				}
			}

			[HarmonyPostfix]
			[HarmonyPatch(typeof(Minimap), "IsExplored")]
			private static void IsExplored(Minimap __instance, Vector3 worldPos, ref bool __result)
			{
				//IL_0023: Unknown result type (might be due to invalid IL or missing references)
				if (!__result && (Object)(object)Client?.Minimap == (Object)(object)__instance)
				{
					__result = Client.IsInShownLayer(worldPos);
				}
			}

			[HarmonyPrefix]
			[HarmonyPatch(typeof(ZNet), "SaveWorld")]
			private static void SaveWorld()
			{
				Server?.Save();
			}

			[HarmonyPrefix]
			[HarmonyPatch(typeof(ZNet), "OnDestroy")]
			private static void ZNetDestroyed()
			{
				Server?.Save();
				Server = null;
			}
		}

		public const string PluginGuid = "txc.sharedmap";

		public const string PluginName = "TXC Shared Map";

		public const string PluginVersion = "2.1.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> ShareExploration;

		internal static ConfigEntry<bool> SharePins;

		internal static ConfigEntry<bool> ForcePublicPosition;

		internal static ConfigEntry<float> DuplicateRadius;

		internal static ConfigEntry<bool> ShowSharedMap;

		internal static ConfigEntry<KeyCode> PrivateMarkerKey;

		internal static ConfigEntry<KeyCode> PublishMarkerKey;

		internal static ConfigEntry<Color> PublicMarkerColor;

		internal static ConfigEntry<float> SendInterval;

		internal static ConfigEntry<int> MaxMessagesPerTick;

		internal static ConfigEntry<int> MaxSendQueueBytes;

		internal static SharedMapServer Server;

		internal static SharedMapClient Client;

		private static ZNet _serverZNet;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Expected O, but got Unknown
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Expected O, but got Unknown
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Expected O, but got Unknown
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Expected O, but got Unknown
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			ShareExploration = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShareExploration", true, "Server setting: share explored map areas between all players who have the mod.");
			SharePins = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "SharePins", true, "Server setting: allow public markers (the 5 player pin icons). Anyone can cross them out; only the owner's account or an admin can delete them. Deletions are logged on the server.");
			ForcePublicPosition = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ForcePublicPosition", false, "Server setting: every player is shown on the map (vanilla 'Visible on map' forced on), including players without the mod. Players with the mod get the checkbox ticked and locked.");
			DuplicateRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "DuplicateRadius", 20f, new ConfigDescription("Server setting: a new public marker with the same icon and name as an existing one within this many metres is deleted (the older one stays). 0 = off.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			ShowSharedMap = ((BaseUnityPlugin)this).Config.Bind<bool>("Client", "ShowSharedMap", true, "Show the shared map: everyone's exploration (lighter fog) and public markers. Off hides them without changing your own exploration, private markers or cartography-table data. Also the 'Shared map' checkbox on the large map.");
			PrivateMarkerKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Client", "PrivateMarkerKey", (KeyCode)306, "Hold this key while double-clicking the map to place a private marker. A plain double-click places a public one.");
			PublishMarkerKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Client", "PublishMarkerKey", (KeyCode)304, "Hold this key and left-click a private marker, or a greyed-out cartography table marker, to make it public. A plain left-click on a greyed-out marker makes it your own private one (vanilla).");
			PublicMarkerColor = ((BaseUnityPlugin)this).Config.Bind<Color>("Client", "PublicMarkerColor", new Color(1f, 0.85f, 0.55f, 1f), "Tint of public marker icons, to tell them apart from private ones (white = no tint).");
			SendInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Network", "SendInterval", 0.25f, new ConfigDescription("Seconds between send batches.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 5f), Array.Empty<object>()));
			MaxMessagesPerTick = ((BaseUnityPlugin)this).Config.Bind<int>("Network", "MaxMessagesPerTick", 4, new ConfigDescription("Max map messages sent per connection per batch.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 64), Array.Empty<object>()));
			MaxSendQueueBytes = ((BaseUnityPlugin)this).Config.Bind<int>("Network", "MaxSendQueueBytes", 5120, new ConfigDescription("Hold map traffic while a connection's send queue is above this (vanilla stops world sync at 10240), so the map never crowds out gameplay data.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1024, 65536), Array.Empty<object>()));
			_harmony = new Harmony("txc.sharedmap");
			_harmony.PatchAll(typeof(Patches));
		}

		private void Update()
		{
			ZNet instance = ZNet.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				if (instance.IsServer() && (Object)(object)_serverZNet != (Object)(object)instance && ZNet.World != null)
				{
					_serverZNet = instance;
					Server = new SharedMapServer(ZNet.World);
				}
				if (!instance.IsDedicated() && (Object)(object)Minimap.instance != (Object)null && (Object)(object)Client?.Minimap != (Object)(object)Minimap.instance)
				{
					Client = new SharedMapClient(Minimap.instance);
				}
				Server?.Tick();
				Client?.Tick();
			}
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class Protocol
	{
		public const int Version = 2;

		public const int MapSize = 2048;

		public const int Pixels = 4194304;

		public const int Words = 131072;

		public const int BandRows = 64;

		public const int BandCount = 32;

		public const int BandWords = 4096;

		public const int BandPixels = 131072;

		public const int MaxIndicesPerMessage = 16384;

		public const int MaxPinOpsPerMessage = 200;

		public const string Hello = "TXCSharedMap_Hello";

		public const string ClientExplored = "TXCSharedMap_ClientExplored";

		public const string ClientPins = "TXCSharedMap_ClientPins";

		public const string Welcome = "TXCSharedMap_Welcome";

		public const string Band = "TXCSharedMap_Band";

		public const string PinList = "TXCSharedMap_PinList";

		public const string SyncDone = "TXCSharedMap_SyncDone";

		public const string ServerExplored = "TXCSharedMap_ServerExplored";

		public const string ServerPins = "TXCSharedMap_ServerPins";

		public static bool IsSharedType(PinType type)
		{
			//IL_0000: 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)
			//IL_0005: Invalid comparison between Unknown and I4
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Invalid comparison between Unknown and I4
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Invalid comparison between Unknown and I4
			if ((int)type != 0 && (int)type != 1 && (int)type != 2 && (int)type != 3)
			{
				return (int)type == 6;
			}
			return true;
		}

		public static bool IsSet(int[] words, int index)
		{
			return (words[index >> 5] & (1 << index)) != 0;
		}

		public static void Set(int[] words, int index)
		{
			words[index >> 5] |= 1 << index;
		}

		public static void AddBits(int word, int wordIndex, List<int> into)
		{
			int num = 0;
			while (word != 0)
			{
				if ((word & 1) != 0)
				{
					into.Add(wordIndex * 32 + num);
				}
				num++;
				word >>>= 1;
			}
		}

		public static string NormalizeName(string name)
		{
			return string.Join(" ", (name ?? "").Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries)).ToLowerInvariant();
		}

		public static string NormalizeId(string hostName)
		{
			if (string.IsNullOrEmpty(hostName))
			{
				return "";
			}
			for (int i = 0; i < hostName.Length; i++)
			{
				if (!char.IsDigit(hostName[i]))
				{
					return hostName;
				}
			}
			return "Steam_" + hostName;
		}

		public static ZPackage EncodeIndices(List<int> sorted)
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Expected O, but got Unknown
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Expected O, but got Unknown
			byte[] array = new byte[sorted.Count * 5];
			int num = 0;
			int num2 = -1;
			foreach (int item in sorted)
			{
				uint num3 = (uint)(item - num2);
				num2 = item;
				while (num3 >= 128)
				{
					array[num++] = (byte)(num3 | 0x80);
					num3 >>= 7;
				}
				array[num++] = (byte)num3;
			}
			byte[] array2 = new byte[num];
			Buffer.BlockCopy(array, 0, array2, 0, num);
			ZPackage val = new ZPackage();
			val.Write(sorted.Count);
			val.Write(array2);
			ZPackage val2 = new ZPackage();
			val2.WriteCompressed(val);
			return val2;
		}

		public static List<int> DecodeIndices(ZPackage pkg)
		{
			ZPackage obj = pkg.ReadCompressedPackage();
			int num = obj.ReadInt();
			byte[] array = obj.ReadByteArray();
			List<int> list = new List<int>(num);
			int num2 = 0;
			int num3 = -1;
			while (list.Count < num && num2 < array.Length)
			{
				uint num4 = 0u;
				int num5 = 0;
				byte b;
				do
				{
					b = array[num2++];
					num4 |= (uint)((b & 0x7F) << num5);
					num5 += 7;
				}
				while ((b & 0x80) != 0 && num2 < array.Length);
				num3 += (int)num4;
				if (num3 < 0 || num3 >= 4194304)
				{
					break;
				}
				list.Add(num3);
			}
			return list;
		}

		public static ZPackage EncodePinOps(List<PinOp> ops)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			//IL_0041: 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_004e: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(ops.Count);
			foreach (PinOp op in ops)
			{
				op.Write(val);
			}
			ZPackage val2 = new ZPackage();
			val2.WriteCompressed(val);
			return val2;
		}

		public static List<PinOp> DecodePinOps(ZPackage pkg)
		{
			ZPackage val = pkg.ReadCompressedPackage();
			int num = val.ReadInt();
			List<PinOp> list = new List<PinOp>(num);
			for (int i = 0; i < num; i++)
			{
				list.Add(PinOp.Read(val));
			}
			return list;
		}

		public static List<T> Take<T>(List<T> list, int max)
		{
			int count = Math.Min(max, list.Count);
			List<T> range = list.GetRange(0, count);
			list.RemoveRange(0, count);
			return range;
		}
	}
	internal readonly struct PinKey : IEquatable<PinKey>
	{
		public readonly int Type;

		public readonly int X;

		public readonly int Z;

		public PinKey(int type, int x, int z)
		{
			Type = type;
			X = x;
			Z = z;
		}

		public PinKey(int type, Vector3 pos)
			: this(type, Mathf.RoundToInt(pos.x * 2f), Mathf.RoundToInt(pos.z * 2f))
		{
		}//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)


		public static PinKey Of(PinData pin)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected I4, but got Unknown
			return new PinKey((int)pin.m_type, pin.m_pos);
		}

		public bool Equals(PinKey other)
		{
			if (Type == other.Type && X == other.X)
			{
				return Z == other.Z;
			}
			return false;
		}

		public override bool Equals(object obj)
		{
			if (obj is PinKey other)
			{
				return Equals(other);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return (((Type * 397) ^ X) * 397) ^ Z;
		}

		public static PinKey Read(ZPackage pkg)
		{
			return new PinKey(pkg.ReadInt(), pkg.ReadInt(), pkg.ReadInt());
		}
	}
	internal enum PinOpKind : byte
	{
		Upsert,
		Remove,
		Check,
		Duplicate
	}
	internal sealed class PinOp
	{
		public PinOpKind Kind;

		public int Type;

		public Vector3 Pos;

		public string Name = "";

		public bool Checked;

		public string Author = "";

		public string OwnerId = "";

		public string OwnerName = "";

		public PinKey Key => new PinKey(Type, Pos);

		public void Write(ZPackage pkg)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			pkg.Write((byte)Kind);
			pkg.Write(Type);
			pkg.Write(Pos);
			pkg.Write(Name ?? "");
			pkg.Write(Checked);
			pkg.Write(Author ?? "");
			pkg.Write(OwnerId ?? "");
			pkg.Write(OwnerName ?? "");
		}

		public static PinOp Read(ZPackage pkg)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			return new PinOp
			{
				Kind = (PinOpKind)pkg.ReadByte(),
				Type = pkg.ReadInt(),
				Pos = pkg.ReadVector3(),
				Name = pkg.ReadString(),
				Checked = pkg.ReadBool(),
				Author = pkg.ReadString(),
				OwnerId = pkg.ReadString(),
				OwnerName = pkg.ReadString()
			};
		}

		public PinOp With(PinOpKind kind)
		{
			PinOp obj = (PinOp)MemberwiseClone();
			obj.Kind = kind;
			return obj;
		}
	}
	internal sealed class SharedMapClient
	{
		private enum Stage
		{
			Waiting,
			HelloSent,
			Syncing,
			Live,
			Disabled
		}

		private sealed class PublicPin
		{
			public PinOp Data;

			public PinData Pin;
		}

		private const float HelloTimeout = 60f;

		private const float ScanInterval = 2f;

		private const float CheckPollInterval = 0.5f;

		private const int BlockApplyThreshold = 256;

		private static readonly FieldRef<Minimap, BitArray> ExploredRef = AccessTools.FieldRefAccess<Minimap, BitArray>("m_explored");

		private static readonly FieldRef<Minimap, BitArray> ExploredOthersRef = AccessTools.FieldRefAccess<Minimap, BitArray>("m_exploredOthers");

		private static readonly FieldRef<Minimap, Texture2D> FogRef = AccessTools.FieldRefAccess<Minimap, Texture2D>("m_fogTexture");

		private static readonly FieldRef<Minimap, List<PinData>> PinsRef = AccessTools.FieldRefAccess<Minimap, List<PinData>>("m_pins");

		private static readonly FieldRef<Minimap, PinData> NamePinRef = AccessTools.FieldRefAccess<Minimap, PinData>("m_namePin");

		private static readonly FieldRef<Minimap, bool> HasGeneratedRef = AccessTools.FieldRefAccess<Minimap, bool>("m_hasGenerated");

		private static readonly Func<Minimap, PinData> ClosestPinToCursor = AccessTools.MethodDelegate<Func<Minimap, PinData>>(AccessTools.Method(typeof(Minimap), "GetClosestPinToCursor", (Type[])null, (Type[])null), (object)null, true);

		private static readonly FieldRef<Minimap, float> SharedFadeRef = AccessTools.FieldRefAccess<Minimap, float>("m_sharedMapDataFade");

		private static readonly FieldRef<Minimap, Material> SmallShaderRef = AccessTools.FieldRefAccess<Minimap, Material>("m_mapSmallShader");

		private static readonly FieldRef<Minimap, Material> LargeShaderRef = AccessTools.FieldRefAccess<Minimap, Material>("m_mapLargeShader");

		private readonly Minimap _minimap;

		private Stage _stage;

		private bool _shareExploration;

		private bool _sharePins;

		private bool _forcePublicPosition;

		private string _myId = "";

		private bool _isAdmin;

		private float _helloTime;

		private float _nextScan;

		private float _nextCheckPoll;

		private float _nextFlush;

		private readonly int[] _layer = new int[131072];

		private readonly int[] _scan = new int[131072];

		private readonly int[] _scanOthers = new int[131072];

		private bool _fadeForced;

		private float _duplicateRadius = 20f;

		private float _nextDedupe;

		private PinData _lastPlaced;

		private bool _layerShown = true;

		private readonly Dictionary<PinKey, PublicPin> _public = new Dictionary<PinKey, PublicPin>();

		private readonly List<int> _pendingExplored = new List<int>();

		private readonly List<PinOp> _pendingPins = new List<PinOp>();

		private int _receivedPixels;

		private PinData _placedPin;

		private bool _placedPrivate;

		private string _typedName = "";

		private bool _nameCancelled;

		private bool _internalRemove;

		private Toggle _showToggle;

		internal bool UserRemoving;

		public Minimap Minimap => _minimap;

		private bool Live => _stage == Stage.Live;

		public SharedMapClient(Minimap minimap)
		{
			_minimap = minimap;
		}

		internal static PinData NamePin(Minimap minimap)
		{
			return NamePinRef.Invoke(minimap);
		}

		public void Tick()
		{
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Expected O, but got Unknown
			if (_stage == Stage.Disabled || !HasGeneratedRef.Invoke(_minimap) || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				return;
			}
			if (!ZNet.instance.IsServer())
			{
				ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
				if (serverPeer == null || !serverPeer.IsReady())
				{
					return;
				}
			}
			if (_forcePublicPosition)
			{
				ZNet.instance.SetPublicReferencePosition(true);
				_minimap.m_publicPosition.isOn = true;
				((Selectable)_minimap.m_publicPosition).interactable = false;
			}
			FinishPlacement();
			float time = Time.time;
			switch (_stage)
			{
			case Stage.Waiting:
			{
				if (ExploredRef.Invoke(_minimap).Length != 4194304)
				{
					Plugin.Log.LogWarning((object)$"Minimap size is {ExploredRef.Invoke(_minimap).Length} pixels, expected {4194304}; map sharing disabled.");
					_stage = Stage.Disabled;
					break;
				}
				_stage = Stage.HelloSent;
				_helloTime = time;
				ZPackage val = new ZPackage();
				val.Write(2);
				SendToServer("TXCSharedMap_Hello", val);
				break;
			}
			case Stage.HelloSent:
				if (time - _helloTime > 60f)
				{
					Plugin.Log.LogInfo((object)"Server did not answer: it doesn't run TXC SharedMap. Map sharing is off for this session.");
					_stage = Stage.Disabled;
				}
				break;
			case Stage.Live:
				EnsureShowToggle();
				if (_layerShown != Plugin.ShowSharedMap.Value)
				{
					SetLayerShown(Plugin.ShowSharedMap.Value);
				}
				if (_shareExploration && time >= _nextScan)
				{
					_nextScan = time + 2f;
					ScanExplored();
				}
				if (time >= _nextDedupe)
				{
					_nextDedupe = time + 1f;
					DedupeLocalPins();
				}
				if (_sharePins && time >= _nextCheckPoll)
				{
					_nextCheckPoll = time + 0.5f;
					PollChecks();
				}
				if (time >= _nextFlush)
				{
					_nextFlush = time + Plugin.SendInterval.Value;
					Flush();
				}
				break;
			case Stage.Syncing:
				break;
			}
		}

		public void Handle(string method, ZPackage pkg)
		{
			if (_stage == Stage.Disabled || _stage == Stage.Waiting)
			{
				return;
			}
			try
			{
				switch (method)
				{
				case "TXCSharedMap_Welcome":
					OnWelcome(pkg);
					break;
				case "TXCSharedMap_Band":
					OnBand(pkg);
					break;
				case "TXCSharedMap_PinList":
					foreach (PinOp item in Protocol.DecodePinOps(pkg))
					{
						ApplyPinOp(item);
					}
					Plugin.Log.LogInfo((object)$"Received {_public.Count} public marker(s).");
					break;
				case "TXCSharedMap_SyncDone":
					_stage = Stage.Live;
					Plugin.Log.LogInfo((object)$"Shared map sync complete: {_receivedPixels} shared map pixel(s) received.");
					break;
				case "TXCSharedMap_ServerExplored":
					AddToLayer(Protocol.DecodeIndices(pkg));
					break;
				case "TXCSharedMap_ServerPins":
				{
					foreach (PinOp item2 in Protocol.DecodePinOps(pkg))
					{
						ApplyPinOp(item2);
					}
					break;
				}
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Error handling {method}: {arg}");
			}
		}

		private void OnWelcome(ZPackage pkg)
		{
			int num = pkg.ReadInt();
			if (num != 2)
			{
				Plugin.Log.LogWarning((object)$"Server runs TXC SharedMap protocol v{num}, this client v{2}: map sharing disabled. Both need the same mod version.");
				_stage = Stage.Disabled;
				return;
			}
			int num2 = pkg.ReadInt();
			_shareExploration = pkg.ReadBool();
			_sharePins = pkg.ReadBool();
			_forcePublicPosition = pkg.ReadBool();
			_myId = pkg.ReadString();
			_isAdmin = pkg.ReadBool();
			_duplicateRadius = ((pkg.GetPos() < pkg.Size()) ? pkg.ReadSingle() : 20f);
			if (num2 != 2048)
			{
				Plugin.Log.LogWarning((object)$"Server map size {num2}, expected {2048}: map sharing disabled.");
				_stage = Stage.Disabled;
				return;
			}
			_layerShown = Plugin.ShowSharedMap.Value;
			_stage = Stage.Syncing;
			Plugin.Log.LogInfo((object)string.Format("Server shares exploration: {0}, markers: {1}, forces 'Visible on map': {2}. You: {3}{4}. Receiving shared map...", _shareExploration, _sharePins, _forcePublicPosition, _myId, _isAdmin ? " (admin)" : ""));
		}

		private void OnBand(ZPackage pkg)
		{
			ZPackage obj = pkg.ReadCompressedPackage();
			int num = obj.ReadInt();
			byte[] array = obj.ReadByteArray();
			if (num < 0 || num >= 32 || array.Length != 16384)
			{
				return;
			}
			int[] array2 = new int[4096];
			Buffer.BlockCopy(array, 0, array2, 0, array.Length);
			List<int> list = new List<int>();
			int num2 = num * 4096;
			for (int i = 0; i < array2.Length; i++)
			{
				if (array2[i] != 0)
				{
					Protocol.AddBits(array2[i], num2 + i, list);
				}
			}
			AddToLayer(list);
		}

		private void AddToLayer(List<int> indices)
		{
			List<int> list = new List<int>();
			foreach (int index in indices)
			{
				if (!Protocol.IsSet(_layer, index))
				{
					Protocol.Set(_layer, index);
					list.Add(index);
				}
			}
			_receivedPixels += list.Count;
			if (_layerShown && list.Count > 0)
			{
				PaintFog(list, shown: true);
			}
		}

		private void PaintFog(List<int> indices, bool shown)
		{
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: 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)
			BitArray bitArray = ExploredOthersRef.Invoke(_minimap);
			Texture2D val = FogRef.Invoke(_minimap);
			indices.Sort();
			int num = 0;
			while (num < indices.Count)
			{
				int num2 = indices[num] / 131072;
				int i;
				for (i = num; i < indices.Count && indices[i] / 131072 == num2; i++)
				{
				}
				if (i - num > 256)
				{
					int num3 = num2 * 64;
					Color[] pixels = val.GetPixels(0, num3, 2048, 64);
					for (int j = num; j < i; j++)
					{
						int num4 = indices[j];
						pixels[num4 - num2 * 131072].g = ((shown || bitArray[num4]) ? 0f : 1f);
					}
					val.SetPixels(0, num3, 2048, 64, pixels);
				}
				else
				{
					for (int k = num; k < i; k++)
					{
						int num5 = indices[k];
						int num6 = num5 % 2048;
						int num7 = num5 / 2048;
						Color pixel = val.GetPixel(num6, num7);
						pixel.g = ((shown || bitArray[num5]) ? 0f : 1f);
						val.SetPixel(num6, num7, pixel);
					}
				}
				num = i;
			}
			val.Apply();
		}

		private void PaintWholeLayer(bool shown)
		{
			List<int> list = new List<int>();
			for (int i = 0; i < 131072; i++)
			{
				if (_layer[i] != 0)
				{
					Protocol.AddBits(_layer[i], i, list);
				}
			}
			if (list.Count > 0)
			{
				PaintFog(list, shown);
			}
		}

		private void SetLayerShown(bool shown)
		{
			_layerShown = shown;
			PaintWholeLayer(shown);
			foreach (PublicPin value in _public.Values)
			{
				if (shown && value.Pin == null)
				{
					value.Pin = AddLocalPin(value.Data);
				}
				else if (!shown && value.Pin != null)
				{
					RemoveLocalPin(value.Pin);
					value.Pin = null;
				}
			}
			Plugin.Log.LogInfo((object)(shown ? "Shared map shown." : "Shared map hidden."));
		}

		private void ApplyPinOp(PinOp op)
		{
			if (!Protocol.IsSharedType((PinType)op.Type))
			{
				return;
			}
			PinKey key = op.Key;
			_public.TryGetValue(key, out var value);
			if (op.Kind == PinOpKind.Remove || op.Kind == PinOpKind.Duplicate)
			{
				if (value != null)
				{
					if (value.Pin != null)
					{
						RemoveLocalPin(value.Pin);
					}
					_public.Remove(key);
				}
				if (op.Kind == PinOpKind.Duplicate)
				{
					string text = (string.IsNullOrEmpty(op.Name) ? "this icon" : ("\"" + op.Name + "\""));
					Message("A public marker " + text + " with the same icon already exists nearby; yours was removed.");
				}
				return;
			}
			if (value == null)
			{
				value = new PublicPin();
				_public[key] = value;
				RemoveLegacyCopy(key);
			}
			else if (value.Pin != null && value.Pin.m_name != op.Name)
			{
				RemoveLocalPin(value.Pin);
				value.Pin = null;
			}
			value.Data = op;
			if (_layerShown)
			{
				if (value.Pin == null)
				{
					value.Pin = AddLocalPin(op);
				}
				else
				{
					value.Pin.m_checked = op.Checked;
				}
			}
		}

		private void RemoveLegacyCopy(PinKey key)
		{
			foreach (PinData item in PinsRef.Invoke(_minimap).ToList())
			{
				if (item.m_save && PinKey.Of(item).Equals(key) && !IsPublic(item))
				{
					RemoveLocalPin(item);
				}
			}
		}

		private PinData AddLocalPin(PinOp op)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: 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_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			PlatformUserID val = (PlatformUserID)(string.IsNullOrEmpty(op.Author) ? PlatformUserID.None : new PlatformUserID(op.Author));
			return _minimap.AddPin(op.Pos, (PinType)op.Type, op.Name, true, op.Checked, 0L, val);
		}

		private void RemoveLocalPin(PinData pin)
		{
			_internalRemove = true;
			try
			{
				_minimap.RemovePin(pin);
			}
			finally
			{
				_internalRemove = false;
			}
		}

		public void OnPinPlaced(PinData pin)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			FinishPlacement(force: true);
			_placedPin = pin;
			_placedPrivate = ZInput.GetKey(Plugin.PrivateMarkerKey.Value, true);
			_typedName = "";
			_nameCancelled = false;
		}

		private static bool CancelPressed()
		{
			if (!ZInput.GetKeyDown((KeyCode)27, true))
			{
				return ZInput.GetButton("JoyButtonB");
			}
			return true;
		}

		private void FinishPlacement(bool force = false)
		{
			PinData placedPin = _placedPin;
			if (placedPin == null)
			{
				return;
			}
			if (!force && NamePinRef.Invoke(_minimap) == placedPin)
			{
				_typedName = ((TMP_InputField)_minimap.m_nameInput).text ?? "";
				_nameCancelled |= CancelPressed();
				return;
			}
			_placedPin = null;
			placedPin = (_lastPlaced = ApplyTypedName(placedPin, _nameCancelled || CancelPressed()));
			if (!_placedPrivate && CanPublish(placedPin))
			{
				Publish(placedPin);
			}
		}

		private bool CanPublish(PinData pin)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			if (Live && _sharePins && _layerShown && pin.m_save && Protocol.IsSharedType(pin.m_type) && PinsRef.Invoke(_minimap).Contains(pin))
			{
				return !_public.ContainsKey(PinKey.Of(pin));
			}
			return false;
		}

		private void Publish(PinData pin)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected I4, but got Unknown
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: 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_0065: 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)
			pin.m_ownerID = 0L;
			PinOp obj = new PinOp
			{
				Kind = PinOpKind.Upsert,
				Type = (int)pin.m_type,
				Pos = pin.m_pos,
				Name = (pin.m_name ?? ""),
				Checked = pin.m_checked,
				Author = ((object)(((PlatformUserID)(ref pin.m_author)).IsValid ? pin.m_author : ((IUser)PlatformManager.DistributionPlatform.LocalUser).PlatformUserID)/*cast due to .constrained prefix*/).ToString(),
				OwnerId = _myId
			};
			Game instance = Game.instance;
			object obj2;
			if (instance == null)
			{
				obj2 = null;
			}
			else
			{
				PlayerProfile playerProfile = instance.GetPlayerProfile();
				obj2 = ((playerProfile != null) ? playerProfile.GetName() : null);
			}
			if (obj2 == null)
			{
				obj2 = "";
			}
			obj.OwnerName = (string)obj2;
			PinOp pinOp = obj;
			_public[PinKey.Of(pin)] = new PublicPin
			{
				Data = pinOp,
				Pin = pin
			};
			_pendingPins.Add(pinOp);
		}

		public bool OnMapLeftClick()
		{
			//IL_0005: 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)
			if (!ZInput.GetKey(Plugin.PublishMarkerKey.Value, true))
			{
				return true;
			}
			PinData val = ClosestPinToCursor(_minimap);
			if (val == null || IsPublic(val))
			{
				return true;
			}
			if (!CanPublish(val))
			{
				if (Live && Protocol.IsSharedType(val.m_type))
				{
					Message((!_layerShown) ? "Turn on \"Server shared map\" to make markers public." : "This marker can't be made public.");
				}
				return true;
			}
			Publish(val);
			return false;
		}

		private PinData ApplyTypedName(PinData pin, bool cancelled)
		{
			//IL_0071: 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_0090: Unknown result type (might be due to invalid IL or missing references)
			string text = (_typedName ?? "").Replace('$', ' ').Replace('<', ' ').Replace('>', ' ');
			if (cancelled || !string.IsNullOrEmpty(pin.m_name) || text.Trim().Length == 0 || !PinsRef.Invoke(_minimap).Contains(pin))
			{
				return pin;
			}
			RemoveLocalPin(pin);
			return _minimap.AddPin(pin.m_pos, pin.m_type, text, pin.m_save, pin.m_checked, pin.m_ownerID, pin.m_author);
		}

		private void DedupeLocalPins()
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			if (_duplicateRadius <= 0f)
			{
				return;
			}
			PinData val = NamePinRef.Invoke(_minimap);
			List<PinData> obj = PinsRef.Invoke(_minimap);
			List<KeyValuePair<int, PinData>> list = new List<KeyValuePair<int, PinData>>();
			foreach (PinData item in obj)
			{
				if (item.m_save && item != val && item != _placedPin && Protocol.IsSharedType(item.m_type))
				{
					int key = ((!IsPublic(item)) ? ((item.m_ownerID == 0L) ? 1 : 2) : 0);
					list.Add(new KeyValuePair<int, PinData>(key, item));
				}
			}
			if (list.Count < 2)
			{
				return;
			}
			List<KeyValuePair<string, PinData>> list2 = new List<KeyValuePair<string, PinData>>();
			int num = 0;
			foreach (KeyValuePair<int, PinData> item2 in list.OrderBy((KeyValuePair<int, PinData> c) => c.Key))
			{
				PinData pin = item2.Value;
				string name = Protocol.NormalizeName(pin.m_name);
				if (!list2.Any((KeyValuePair<string, PinData> k) => k.Value.m_type == pin.m_type && k.Key == name && Utils.DistanceXZ(k.Value.m_pos, pin.m_pos) <= _duplicateRadius) || item2.Key == 0)
				{
					list2.Add(new KeyValuePair<string, PinData>(name, pin));
					continue;
				}
				RemoveLocalPin(pin);
				num++;
				if (pin == _lastPlaced)
				{
					_lastPlaced = null;
					string text = (string.IsNullOrEmpty(pin.m_name) ? "this icon" : ("\"" + pin.m_name + "\""));
					Message("A marker " + text + " with the same icon already exists nearby; yours was removed.");
				}
			}
			if (num > 0)
			{
				Plugin.Log.LogInfo((object)$"Removed {num} duplicate marker(s) from your map.");
			}
		}

		private void PollChecks()
		{
			foreach (PublicPin value in _public.Values)
			{
				if (value.Pin != null && value.Pin.m_checked != value.Data.Checked)
				{
					value.Data.Checked = value.Pin.m_checked;
					_pendingPins.Add(value.Data.With(PinOpKind.Check));
				}
			}
		}

		public bool AllowRemove(PinData pin)
		{
			if (_internalRemove || !IsPublic(pin, out var key, out var entry))
			{
				return true;
			}
			if (!UserRemoving)
			{
				return false;
			}
			if (!CanDelete(entry.Data))
			{
				string text = (string.IsNullOrEmpty(entry.Data.OwnerName) ? "its owner" : entry.Data.OwnerName);
				Message("Only " + text + " or an admin can delete this marker.");
				return false;
			}
			_public.Remove(key);
			_pendingPins.Add(entry.Data.With(PinOpKind.Remove));
			return true;
		}

		private bool CanDelete(PinOp pin)
		{
			if (!_isAdmin)
			{
				if (!string.IsNullOrEmpty(pin.OwnerId))
				{
					return pin.OwnerId == _myId;
				}
				return false;
			}
			return true;
		}

		private bool IsPublic(PinData pin)
		{
			PinKey key;
			PublicPin entry;
			return IsPublic(pin, out key, out entry);
		}

		private bool IsPublic(PinData pin, out PinKey key, out PublicPin entry)
		{
			key = PinKey.Of(pin);
			if (_public.TryGetValue(key, out entry))
			{
				return entry.Pin == pin;
			}
			return false;
		}

		public List<PinData> BeginExcludePublic()
		{
			List<PinData> list = new List<PinData>();
			foreach (PublicPin value in _public.Values)
			{
				if (value.Pin != null && value.Pin.m_save)
				{
					value.Pin.m_save = false;
					list.Add(value.Pin);
				}
			}
			return list;
		}

		public static void EndExcludePublic(List<PinData> excluded)
		{
			if (excluded == null)
			{
				return;
			}
			foreach (PinData item in excluded)
			{
				item.m_save = true;
			}
		}

		public void ApplySharedFade()
		{
			bool flag = Live && _layerShown;
			if (flag || _fadeForced)
			{
				float num = (flag ? 1f : SharedFadeRef.Invoke(_minimap));
				Material obj = SmallShaderRef.Invoke(_minimap);
				if (obj != null)
				{
					obj.SetFloat("_SharedFade", num);
				}
				Material obj2 = LargeShaderRef.Invoke(_minimap);
				if (obj2 != null)
				{
					obj2.SetFloat("_SharedFade", num);
				}
				_fadeForced = flag;
			}
		}

		public void OnSharedMapDataReset()
		{
			if (Live && _layerShown)
			{
				PaintWholeLayer(shown: true);
			}
		}

		public void TintPublicPins()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: 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)
			Color value = Plugin.PublicMarkerColor.Value;
			foreach (PublicPin value2 in _public.Values)
			{
				PinData pin = value2.Pin;
				if ((Object)(object)pin?.m_iconElement != (Object)null)
				{
					((Graphic)pin.m_iconElement).color = value;
				}
			}
		}

		public bool IsInShownLayer(Vector3 worldPos)
		{
			//IL_0010: 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)
			if (!_layerShown)
			{
				return false;
			}
			int num = 1024;
			int num2 = Utils.RoundToInt(worldPos.x / _minimap.m_pixelSize + (float)num);
			int num3 = Utils.RoundToInt(worldPos.z / _minimap.m_pixelSize + (float)num);
			if (num2 >= 0 && num2 < 2048 && num3 >= 0 && num3 < 2048)
			{
				return Protocol.IsSet(_layer, num3 * 2048 + num2);
			}
			return false;
		}

		private void EnsureShowToggle()
		{
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Expected O, but got Unknown
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: 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_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: 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_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Expected O, but got Unknown
			if ((Object)(object)_showToggle != (Object)null || (Object)(object)_minimap.m_publicPosition == (Object)null)
			{
				if ((Object)(object)_showToggle != (Object)null && _showToggle.isOn != Plugin.ShowSharedMap.Value)
				{
					_showToggle.SetIsOnWithoutNotify(Plugin.ShowSharedMap.Value);
				}
				return;
			}
			Transform transform = ((Component)_minimap.m_publicPosition).transform;
			Transform val = FindCartographyControl();
			Transform val2 = transform;
			Transform val3 = val;
			if ((Object)(object)val != (Object)null)
			{
				Transform ancestor = CommonAncestor(transform, val);
				val2 = ChildOn(ancestor, transform);
				val3 = ChildOn(ancestor, val);
			}
			GameObject val4 = Object.Instantiate<GameObject>(((Component)val2).gameObject, val2.parent);
			((Object)val4).name = "TXCSharedMapToggle";
			if ((Object)(object)val3 != (Object)null && (Object)(object)((Component)val2.parent).GetComponent<LayoutGroup>() != (Object)null)
			{
				val4.transform.SetSiblingIndex(val3.GetSiblingIndex());
			}
			else if ((Object)(object)val3 != (Object)null)
			{
				Vector3 val5 = val3.position - val2.position;
				val4.transform.position = val3.position + val5;
			}
			else
			{
				RectTransform val6 = (RectTransform)val4.transform;
				RectTransform val7 = (RectTransform)val2;
				Vector2 anchoredPosition = val7.anchoredPosition;
				Rect rect = val7.rect;
				val6.anchoredPosition = anchoredPosition + new Vector2(0f, 2f * (((Rect)(ref rect)).height + 6f));
			}
			_showToggle = val4.GetComponentInChildren<Toggle>(true);
			_showToggle.onValueChanged = new ToggleEvent();
			((Selectable)_showToggle).interactable = true;
			_showToggle.SetIsOnWithoutNotify(Plugin.ShowSharedMap.Value);
			((UnityEvent<bool>)(object)_showToggle.onValueChanged).AddListener((UnityAction<bool>)delegate(bool value)
			{
				Plugin.ShowSharedMap.Value = value;
			});
			TMP_Text[] componentsInChildren = val4.GetComponentsInChildren<TMP_Text>(true);
			for (int num = 0; num < componentsInChildren.Length; num++)
			{
				componentsInChildren[num].text = "Server shared map";
			}
		}

		private Transform FindCartographyControl()
		{
			Selectable[] componentsInChildren = _minimap.m_largeRoot.GetComponentsInChildren<Selectable>(true);
			foreach (Selectable val in componentsInChildren)
			{
				Button val2 = (Button)(object)((val is Button) ? val : null);
				UnityEventBase val3 = (UnityEventBase)((val2 != null) ? ((object)val2.onClick) : ((object)((Toggle)(((val is Toggle) ? val : null)?)).onValueChanged));
				if (val3 == null)
				{
					continue;
				}
				for (int j = 0; j < val3.GetPersistentEventCount(); j++)
				{
					if (val3.GetPersistentMethodName(j) == "OnToggleSharedMapData")
					{
						return ((Component)val).transform;
					}
				}
			}
			Plugin.Log.LogWarning((object)"Could not find the cartography 'Shared map' control; placing the Server shared map checkbox two rows up.");
			return null;
		}

		private static Transform CommonAncestor(Transform a, Transform b)
		{
			Transform val = a;
			while ((Object)(object)val != (Object)null)
			{
				if (b.IsChildOf(val))
				{
					return val;
				}
				val = val.parent;
			}
			return a.root;
		}

		private static Transform ChildOn(Transform ancestor, Transform descendant)
		{
			Transform val = descendant;
			while ((Object)(object)val.parent != (Object)null && (Object)(object)val.parent != (Object)(object)ancestor)
			{
				val = val.parent;
			}
			return val;
		}

		private static void Message(string text)
		{
			Player localPlayer = Player.m_localPlayer;
			if (localPlayer != null)
			{
				((Character)localPlayer).Message((MessageType)2, text, 0, (Sprite)null, false);
			}
		}

		private void ScanExplored()
		{
			ExploredRef.Invoke(_minimap).CopyTo(_scan, 0);
			ExploredOthersRef.Invoke(_minimap).CopyTo(_scanOthers, 0);
			for (int i = 0; i < 131072; i++)
			{
				int num = (_scan[i] | _scanOthers[i]) & ~_layer[i];
				if (num != 0)
				{
					_layer[i] |= num;
					Protocol.AddBits(num, i, _pendingExplored);
				}
			}
		}

		private void Flush()
		{
			int num = Plugin.MaxMessagesPerTick.Value;
			while (num > 0 && CanSendToServer())
			{
				if (_pendingPins.Count > 0)
				{
					SendToServer("TXCSharedMap_ClientPins", Protocol.EncodePinOps(Protocol.Take(_pendingPins, 200)));
				}
				else
				{
					if (_pendingExplored.Count <= 0)
					{
						break;
					}
					List<int> list = Protocol.Take(_pendingExplored, 16384);
					list.Sort();
					SendToServer("TXCSharedMap_ClientExplored", Protocol.EncodeIndices(list));
				}
				num--;
			}
		}

		private static bool CanSendToServer()
		{
			if (ZNet.instance.IsServer())
			{
				return true;
			}
			ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
			if (serverPeer != null && serverPeer.IsReady())
			{
				return serverPeer.m_socket.GetSendQueueSize() < Plugin.MaxSendQueueBytes.Value;
			}
			return false;
		}

		private static void SendToServer(string method, ZPackage pkg)
		{
			if (ZNet.instance.IsServer())
			{
				pkg.SetPos(0);
				Plugin.Server?.Handle(ZNet.GetUID(), method, pkg);
				return;
			}
			ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
			if (serverPeer != null)
			{
				serverPeer.m_rpc.Invoke(method, new object[1] { pkg });
			}
		}
	}
	internal sealed class SharedMapServer
	{
		private sealed class Peer
		{
			public long Uid;

			public string Name;

			public string Id;

			public bool IsAdmin;

			public readonly Queue<KeyValuePair<string, ZPackage>> Initial = new Queue<KeyValuePair<string, ZPackage>>();

			public readonly List<int> Explored = new List<int>();

			public readonly List<PinOp> PinOps = new List<PinOp>();
		}

		private const string FileMagic = "TXCSharedMap";

		private const int FileVersion = 2;

		private readonly int[] _explored = new int[131072];

		private readonly Dictionary<PinKey, PinOp> _pins = new Dictionary<PinKey, PinOp>();

		private readonly Dictionary<long, Peer> _peers = new Dictionary<long, Peer>();

		private readonly string _savePath;

		private bool _dirty;

		private float _nextFlush;

		public SharedMapServer(World world)
		{
			_savePath = GetSavePath(world);
			Load();
			RemoveStoredDuplicates();
		}

		private static string GetSavePath(World world)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			if ((int)world.m_fileSource == 2)
			{
				return Path.ChangeExtension(world.GetDBPath(), ".txcsharedmap");
			}
			string text = Path.Combine(Paths.ConfigPath, "TXC.SharedMap");
			Directory.CreateDirectory(text);
			return Path.Combine(text, $"{world.m_name}_{world.m_uid}.txcsharedmap");
		}

		public void Handle(long uid, string method, ZPackage pkg)
		{
			try
			{
				switch (method)
				{
				case "TXCSharedMap_Hello":
					OnHello(uid, pkg);
					break;
				case "TXCSharedMap_ClientExplored":
					OnClientExplored(uid, pkg);
					break;
				case "TXCSharedMap_ClientPins":
					OnClientPins(uid, pkg);
					break;
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Error handling {method} from {DescribePeer(uid)}: {arg}");
			}
		}

		private void OnHello(long uid, ZPackage pkg)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Expected O, but got Unknown
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Expected O, but got Unknown
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Expected O, but got Unknown
			int num = pkg.ReadInt();
			Peer peer = new Peer
			{
				Uid = uid,
				Name = PlayerName(uid),
				Id = AccountId(uid),
				IsAdmin = IsAdmin(uid)
			};
			ZPackage val = new ZPackage();
			val.Write(2);
			val.Write(2048);
			val.Write(Plugin.ShareExploration.Value);
			val.Write(Plugin.SharePins.Value);
			val.Write(Plugin.ForcePublicPosition.Value);
			val.Write(peer.Id);
			val.Write(peer.IsAdmin);
			val.Write(Plugin.DuplicateRadius.Value);
			Send(uid, "TXCSharedMap_Welcome", val);
			if (num != 2)
			{
				_peers.Remove(uid);
				Plugin.Log.LogWarning((object)$"{DescribePeer(uid)} runs protocol v{num}, server runs v{2}: not syncing this player.");
				return;
			}
			int num2 = 0;
			if (Plugin.ShareExploration.Value)
			{
				for (int i = 0; i < 32; i++)
				{
					int num3 = i * 4096;
					bool flag = true;
					for (int j = 0; j < 4096 && flag; j++)
					{
						flag = _explored[num3 + j] == 0;
					}
					if (!flag)
					{
						byte[] array = new byte[16384];
						Buffer.BlockCopy(_explored, num3 * 4, array, 0, array.Length);
						ZPackage val2 = new ZPackage();
						val2.Write(i);
						val2.Write(array);
						ZPackage val3 = new ZPackage();
						val3.WriteCompressed(val2);
						peer.Initial.Enqueue(new KeyValuePair<string, ZPackage>("TXCSharedMap_Band", val3));
						num2++;
					}
				}
			}
			if (Plugin.SharePins.Value)
			{
				peer.Initial.Enqueue(new KeyValuePair<string, ZPackage>("TXCSharedMap_PinList", Protocol.EncodePinOps(_pins.Values.ToList())));
			}
			ZPackage val4 = new ZPackage();
			val4.Write(num2);
			peer.Initial.Enqueue(new KeyValuePair<string, ZPackage>("TXCSharedMap_SyncDone", val4));
			_peers[uid] = peer;
			Plugin.Log.LogInfo((object)string.Format("{0}{1} joined map sharing: sending {2} map band(s), {3} public marker(s).", DescribePeer(uid), peer.IsAdmin ? " [admin]" : "", num2, _pins.Count));
		}

		private void OnClientExplored(long uid, ZPackage pkg)
		{
			if (!Plugin.ShareExploration.Value || !_peers.ContainsKey(uid))
			{
				return;
			}
			List<int> list = new List<int>();
			foreach (int item in Protocol.DecodeIndices(pkg))
			{
				if (!Protocol.IsSet(_explored, item))
				{
					Protocol.Set(_explored, item);
					list.Add(item);
				}
			}
			if (list.Count == 0)
			{
				return;
			}
			_dirty = true;
			foreach (Peer value in _peers.Values)
			{
				if (value.Uid != uid)
				{
					value.Explored.AddRange(list);
				}
			}
		}

		private void OnClientPins(long uid, ZPackage pkg)
		{
			if (!Plugin.SharePins.Value || !_peers.TryGetValue(uid, out var value))
			{
				return;
			}
			foreach (PinOp item in Protocol.DecodePinOps(pkg))
			{
				if (!Protocol.IsSharedType((PinType)item.Type))
				{
					continue;
				}
				PinKey key = item.Key;
				_pins.TryGetValue(key, out var value2);
				switch (item.Kind)
				{
				case PinOpKind.Upsert:
				{
					if (value2 != null)
					{
						value.PinOps.Add(value2.With(PinOpKind.Upsert));
						break;
					}
					PinOp pinOp = FindDuplicate(item, null);
					if (pinOp != null)
					{
						Plugin.Log.LogInfo((object)("Duplicate marker removed: " + Describe(item) + " placed by " + value.Name + " duplicates " + Describe(pinOp) + " by " + pinOp.OwnerName + "."));
						value.PinOps.Add(item.With(PinOpKind.Duplicate));
					}
					else
					{
						value2 = item.With(PinOpKind.Upsert);
						value2.OwnerId = value.Id;
						value2.OwnerName = value.Name;
						_pins[key] = value2;
						_dirty = true;
						Broadcast(uid, value2);
					}
					break;
				}
				case PinOpKind.Check:
					if (value2 != null)
					{
						value2.Checked = item.Checked;
						_dirty = true;
						Broadcast(uid, value2.With(PinOpKind.Upsert));
					}
					break;
				case PinOpKind.Remove:
					if (value2 != null)
					{
						bool flag = !string.IsNullOrEmpty(value2.OwnerId) && value2.OwnerId == value.Id;
						if (!flag && !value.IsAdmin)
						{
							Plugin.Log.LogWarning((object)("Denied: " + value.Name + " tried to delete " + Describe(value2) + ", owned by " + value2.OwnerName + "."));
							value.PinOps.Add(value2.With(PinOpKind.Upsert));
						}
						else
						{
							_pins.Remove(key);
							_dirty = true;
							Plugin.Log.LogInfo((object)("Marker deleted by " + value.Name + (flag ? "" : " (admin)") + ": " + Describe(value2) + ", owned by " + value2.OwnerName + "."));
							Broadcast(uid, value2.With(PinOpKind.Remove));
						}
					}
					break;
				}
			}
		}

		private PinOp FindDuplicate(PinOp pin, PinOp ignore)
		{
			//IL_0062: 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)
			float value = Plugin.DuplicateRadius.Value;
			if (value <= 0f)
			{
				return null;
			}
			string text = NormalizeName(pin.Name);
			foreach (PinOp value2 in _pins.Values)
			{
				if (value2 != ignore && value2.Type == pin.Type && NormalizeName(value2.Name) == text && Utils.DistanceXZ(value2.Pos, pin.Pos) <= value)
				{
					return value2;
				}
			}
			return null;
		}

		private static string NormalizeName(string name)
		{
			return Protocol.NormalizeName(name);
		}

		private void RemoveStoredDuplicates()
		{
			List<PinOp> list = new List<PinOp>();
			foreach (PinOp pin in _pins.Values.ToList())
			{
				float radius = Plugin.DuplicateRadius.Value;
				string name = NormalizeName(pin.Name);
				PinOp pinOp = ((radius <= 0f) ? null : list.FirstOrDefault((PinOp k) => k.Type == pin.Type && NormalizeName(k.Name) == name && Utils.DistanceXZ(k.Pos, pin.Pos) <= radius));
				if (pinOp == null)
				{
					list.Add(pin);
					continue;
				}
				_pins.Remove(pin.Key);
				_dirty = true;
				Plugin.Log.LogInfo((object)("Duplicate marker removed on load: " + Describe(pin) + " by " + pin.OwnerName + " duplicates " + Describe(pinOp) + " by " + pinOp.OwnerName + "."));
			}
		}

		private static string Describe(PinOp pin)
		{
			//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_001e: Unknown result type (might be due to invalid IL or missing references)
			string text = ((WorldGenerator.instance != null) ? ((object)WorldGenerator.instance.GetBiome(pin.Pos)/*cast due to .constrained prefix*/).ToString() : "?");
			string text2 = (string.IsNullOrEmpty(pin.Name) ? "(unnamed)" : ("\"" + pin.Name + "\""));
			return $"{text2} [{(object)(PinType)pin.Type}] in {text} at ({pin.Pos.x:0}, {pin.Pos.z:0})";
		}

		private void Broadcast(long senderUid, PinOp op)
		{
			foreach (Peer value in _peers.Values)
			{
				if (value.Uid != senderUid)
				{
					value.PinOps.Add(op);
				}
			}
		}

		public void Tick()
		{
			if (Time.time < _nextFlush)
			{
				return;
			}
			_nextFlush = Time.time + Plugin.SendInterval.Value;
			foreach (Peer item in _peers.Values.ToList())
			{
				if (item.Uid != ZNet.GetUID() && ZNet.instance.GetPeer(item.Uid) == null)
				{
					_peers.Remove(item.Uid);
				}
				else
				{
					Flush(item);
				}
			}
		}

		private void Flush(Peer peer)
		{
			int num = Plugin.MaxMessagesPerTick.Value;
			while (num > 0 && CanSend(peer.Uid))
			{
				if (peer.Initial.Count > 0)
				{
					KeyValuePair<string, ZPackage> keyValuePair = peer.Initial.Dequeue();
					Send(peer.Uid, keyValuePair.Key, keyValuePair.Value);
				}
				else if (peer.PinOps.Count > 0)
				{
					Send(peer.Uid, "TXCSharedMap_ServerPins", Protocol.EncodePinOps(Protocol.Take(peer.PinOps, 200)));
				}
				else
				{
					if (peer.Explored.Count <= 0)
					{
						break;
					}
					List<int> list = Protocol.Take(peer.Explored, 16384);
					list.Sort();
					Send(peer.Uid, "TXCSharedMap_ServerExplored", Protocol.EncodeIndices(list));
				}
				num--;
			}
		}

		private static bool CanSend(long uid)
		{
			if (uid == ZNet.GetUID())
			{
				return true;
			}
			ZNetPeer peer = ZNet.instance.GetPeer(uid);
			if (peer != null && peer.IsReady())
			{
				return peer.m_socket.GetSendQueueSize() < Plugin.MaxSendQueueBytes.Value;
			}
			return false;
		}

		private static void Send(long uid, string method, ZPackage pkg)
		{
			if (uid == ZNet.GetUID())
			{
				pkg.SetPos(0);
				Plugin.Client?.Handle(method, pkg);
				return;
			}
			ZNetPeer peer = ZNet.instance.GetPeer(uid);
			if (peer != null)
			{
				peer.m_rpc.Invoke(method, new object[1] { pkg });
			}
		}

		private static string PlayerName(long uid)
		{
			if (uid == ZNet.GetUID())
			{
				Game instance = Game.instance;
				object obj;
				if (instance == null)
				{
					obj = null;
				}
				else
				{
					PlayerProfile playerProfile = instance.GetPlayerProfile();
					obj = ((playerProfile != null) ? playerProfile.GetName() : null);
				}
				if (obj == null)
				{
					obj = "host";
				}
				return (string)obj;
			}
			return ZNet.instance.GetPeer(uid)?.m_playerName ?? uid.ToString();
		}

		private static string AccountId(long uid)
		{
			//IL_0012: 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)
			if (uid == ZNet.GetUID())
			{
				return ((object)((IUser)PlatformManager.DistributionPlatform.LocalUser).PlatformUserID/*cast due to .constrained prefix*/).ToString();
			}
			ZNetPeer peer = ZNet.instance.GetPeer(uid);
			return Protocol.NormalizeId((peer != null) ? peer.m_socket.GetHostName() : null);
		}

		private static bool IsAdmin(long uid)
		{
			if (uid == ZNet.GetUID())
			{
				return true;
			}
			ZNetPeer peer = ZNet.instance.GetPeer(uid);
			if (peer != null)
			{
				return ZNet.instance.IsAdmin(peer.m_socket.GetHostName());
			}
			return false;
		}

		private string DescribePeer(long uid)
		{
			if (!_peers.TryGetValue(uid, out var value))
			{
				return PlayerName(uid) + " (" + AccountId(uid) + ")";
			}
			return value.Name + " (" + value.Id + ")";
		}

		public void Save()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Expected O, but got Unknown
			if (!_dirty)
			{
				return;
			}
			try
			{
				ZPackage val = new ZPackage();
				val.Write(2048);
				byte[] array = new byte[524288];
				Buffer.BlockCopy(_explored, 0, array, 0, array.Length);
				val.Write(array);
				val.Write(_pins.Count);
				foreach (PinOp value in _pins.Values)
				{
					value.Write(val);
				}
				ZPackage val2 = new ZPackage();
				val2.Write("TXCSharedMap");
				val2.Write(2);
				val2.WriteCompressed(val);
				string text = _savePath + ".tmp";
				File.WriteAllBytes(text, val2.GetArray());
				if (File.Exists(_savePath))
				{
					File.Copy(_savePath, _savePath + ".old", overwrite: true);
				}
				File.Copy(text, _savePath, overwrite: true);
				File.Delete(text);
				_dirty = false;
				Plugin.Log.LogInfo((object)$"Saved shared map ({_pins.Count} public markers) to {_savePath}");
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Failed to save shared map to {_savePath}: {arg}");
			}
		}

		private void Load()
		{
			//IL_0039: 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_005b: Unknown result type (might be due to invalid IL or missing references)
			if (!File.Exists(_savePath))
			{
				Plugin.Log.LogInfo((object)("No shared map saved yet (" + _savePath + "); starting empty."));
				return;
			}
			try
			{
				ZPackage val = new ZPackage(File.ReadAllBytes(_savePath));
				if (val.ReadString() != "TXCSharedMap")
				{
					throw new InvalidDataException("not a TXC SharedMap file");
				}
				int num = val.ReadInt();
				if (num < 1 || num > 2)
				{
					throw new InvalidDataException($"unsupported file version {num}");
				}
				ZPackage val2 = val.ReadCompressedPackage();
				int num2 = val2.ReadInt();
				if (num2 != 2048)
				{
					throw new InvalidDataException($"map size {num2}, expected {2048}");
				}
				byte[] array = val2.ReadByteArray();
				Buffer.BlockCopy(array, 0, _explored, 0, Math.Min(array.Length, 524288));
				int num3 = val2.ReadInt();
				for (int i = 0; i < num3; i++)
				{
					PinOp pinOp = ((num == 1) ? ReadV1Pin(val2) : PinOp.Read(val2));
					pinOp.Kind = PinOpKind.Upsert;
					_pins[pinOp.Key] = pinOp;
				}
				if (num == 1)
				{
					_dirty = true;
				}
				int num4 = 0;
				int[] explored = _explored;
				for (int j = 0; j < explored.Length; j++)
				{
					for (int num5 = explored[j]; num5 != 0; num5 &= num5 - 1)
					{
						num4++;
					}
				}
				Plugin.Log.LogInfo((object)$"Loaded shared map (file v{num}): {num4} explored pixels, {_pins.Count} public markers.");
			}
			catch (Exception ex)
			{
				string text = $"{_savePath}.broken-{DateTime.Now:yyyyMMdd-HHmmss}";
				Plugin.Log.LogError((object)("Failed to load " + _savePath + " (" + ex.Message + "); moved it to " + text + " and starting empty."));
				try
				{
					File.Move(_savePath, text);
				}
				catch (Exception ex2)
				{
					Plugin.Log.LogError((object)("Could not move it: " + ex2.Message));
				}
				Array.Clear(_explored, 0, _explored.Length);
				_pins.Clear();
			}
		}

		private static PinOp ReadV1Pin(ZPackage pkg)
		{
			//IL_001a: 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)
			pkg.ReadBool();
			return new PinOp
			{
				Type = pkg.ReadInt(),
				Pos = pkg.ReadVector3(),
				Name = pkg.ReadString(),
				Checked = pkg.ReadBool(),
				Author = pkg.ReadString(),
				OwnerName = pkg.ReadString()
			};
		}
	}
}