Decompiled source of MapAutoSync v0.3.0

plugins/MapAutoSync.dll

Decompiled a week ago
using System;
using System.Collections.Concurrent;
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 System.Security;
using System.Security.Permissions;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
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.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: AssemblyCompany("MapAutoSync")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.3.0.0")]
[assembly: AssemblyInformationalVersion("0.3.0+b7b2b3f542e8058c6b4572d77d2e97811b3387fe")]
[assembly: AssemblyProduct("MapAutoSync")]
[assembly: AssemblyTitle("MapAutoSync")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.3.0.0")]
[module: UnverifiableCode]
[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 MapAutoSync
{
	internal static class ClientSync
	{
		private const float JoinDelay = 20f;

		private static float s_readyAt = -1f;

		private static bool s_synced;

		private static int s_lastDay = -1;

		private static bool s_now;

		internal static void Reset()
		{
			s_readyAt = -1f;
			s_synced = false;
			s_lastDay = -1;
			s_now = false;
		}

		internal static void RequestNow()
		{
			s_now = true;
		}

		internal static void Tick()
		{
			if (!((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)Minimap.instance == (Object)null) && !((Object)(object)EnvMan.instance == (Object)null) && ZRoutedRpc.instance != null)
			{
				if (s_readyAt < 0f)
				{
					s_readyAt = Time.time + 20f;
				}
				int day = EnvMan.instance.GetDay();
				bool flag = MapAutoSyncPlugin.AutoSync.Value && ((!s_synced && Time.time >= s_readyAt) || (s_synced && day != s_lastDay));
				if (s_now || flag)
				{
					s_now = false;
					s_synced = true;
					s_lastDay = day;
					Upload();
				}
			}
		}

		private static void Upload()
		{
			try
			{
				byte[] array = Utils.Compress(Minimap.instance.GetSharedMapData((byte[])null));
				Transfer.Send(ZRoutedRpc.instance.GetServerPeerID(), "MapAutoSync", 1, Player.m_localPlayer.GetPlayerID(), array);
				MapAutoSyncPlugin.Log.LogInfo((object)$"sent map to the server: {array.Length / 1024} KB");
			}
			catch (Exception arg)
			{
				MapAutoSyncPlugin.Log.LogError((object)$"map upload failed: {arg}");
			}
		}

		internal static void Merged(byte[] data)
		{
			if (!((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)Minimap.instance == (Object)null))
			{
				bool flag = Minimap.instance.AddSharedMapData(Utils.Decompress(data));
				bool num = MapToggle.SyncDone(flag);
				MapAutoSyncPlugin.Log.LogInfo((object)string.Format("merged the server's map: {0} KB, {1}", data.Length / 1024, flag ? "new areas or pins" : "nothing new"));
				if (!num && MapAutoSyncPlugin.ShowMessage.Value)
				{
					MapBanner.Show(flag ? "Map synced: new areas or pins" : "Map synced", (MessageType)1);
				}
			}
		}
	}
	[BepInPlugin("liekos47.mapautosync", "MapAutoSync", "0.3.0")]
	public class MapAutoSyncPlugin : BaseUnityPlugin
	{
		public const string Guid = "liekos47.mapautosync";

		public const string Name = "MapAutoSync";

		public const string Version = "0.3.0";

		internal static ManualLogSource Log;

		internal static MapAutoSyncPlugin Instance;

		internal static ConfigEntry<bool> AutoSync;

		internal static ConfigEntry<bool> ShowMessage;

		internal static ConfigEntry<string> ButtonLabel;

		internal static ConfigEntry<string> SyncNowLabel;

		internal static ConfigEntry<float> ButtonOffsetX;

		internal static ConfigEntry<float> ButtonOffsetY;

		internal static ConfigEntry<float> BannerSize;

		internal static ConfigEntry<float> BannerOffset;

		private Harmony harmony;

		private ZRoutedRpc registeredFor;

		private ServerStore store;

		private string storeWorld;

		private float nextTick;

		private readonly HashSet<long> reminded = new HashSet<long>();

		private readonly ConcurrentQueue<Action> mainThread = new ConcurrentQueue<Action>();

		private void Awake()
		{
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Instance = this;
			AutoSync = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AutoSync", true, "Share your explored map and pins with the server when you join and once every in-game day. This is the tick-box on the large map. The \"Sync now\" button above it works either way.");
			ShowMessage = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowMessage", true, "Tell you when the map has been synced automatically: in large text on the map if it is open, otherwise as a short top-left message. A \"Sync now\" always reports on screen.");
			ButtonLabel = ((BaseUnityPlugin)this).Config.Bind<string>("Button", "Label", "Auto sync map", "The text beside the tick-box on the large map.");
			SyncNowLabel = ((BaseUnityPlugin)this).Config.Bind<string>("Button", "SyncNowLabel", "Sync now", "The text beside the button above the tick-box that syncs the map straight away.");
			ButtonOffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("Button", "OffsetX", 0f, "Moves the tick-box sideways from the \"Visible to other players\" tick-box, in screen units. Applied when the map screen is created (rejoin to see a change).");
			ButtonOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("Button", "OffsetY", 0f, "Moves the tick-box up or down. 0 places it one row above \"Visible to other players\", or above the game's \"Cartography Table\" row when that is showing; use a negative number to move it down.");
			BannerSize = ((BaseUnityPlugin)this).Config.Bind<float>("Message", "Size", 36f, "Text size of the message shown on the large map (\"Syncing map...\", \"Map synced\"). Rejoin to see a change.");
			BannerOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Message", "OffsetFromTop", 90f, "How far below the top of the screen that message sits, in screen units. Rejoin to see a change.");
			harmony = new Harmony(string.Format("{0}.{1}", "liekos47.mapautosync", DateTime.Now.Ticks));
			harmony.PatchAll();
			Log.LogInfo((object)"MapAutoSync 0.3.0 loaded");
		}

		private void Update()
		{
			Action result;
			while (mainThread.TryDequeue(out result))
			{
				try
				{
					result();
				}
				catch (Exception arg)
				{
					Log.LogError((object)$"{arg}");
				}
			}
			MapBanner.Frame();
			if (Time.time < nextTick)
			{
				return;
			}
			nextTick = Time.time + 2f;
			try
			{
				RegisterRpc();
				if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer())
				{
					MapToggle.Ensure();
					ClientSync.Tick();
				}
				else
				{
					ClientSync.Reset();
				}
			}
			catch (Exception arg2)
			{
				Log.LogError((object)$"tick failed: {arg2}");
			}
		}

		private void RegisterRpc()
		{
			if (ZRoutedRpc.instance != null && ZRoutedRpc.instance != registeredFor)
			{
				ZRoutedRpc.instance.m_functions.Remove(StringExtensionMethods.GetStableHashCode("MapAutoSync"));
				ZRoutedRpc.instance.m_functions.Remove(StringExtensionMethods.GetStableHashCode("AutoSyncMap"));
				ZRoutedRpc.instance.Register<ZPackage>("MapAutoSync", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
				{
					OnPackage(sender, pkg, legacy: false);
				});
				ZRoutedRpc.instance.Register<ZPackage>("AutoSyncMap", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
				{
					OnPackage(sender, pkg, legacy: true);
				});
				reminded.Clear();
				registeredFor = ZRoutedRpc.instance;
			}
		}

		private void OnPackage(long sender, ZPackage pkg, bool legacy)
		{
			try
			{
				if (Transfer.Receive(sender, pkg, legacy, out var kind, out var playerId, out var version, out var data))
				{
					bool flag = (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer();
					if (kind == 1 && flag)
					{
						ServerMerge(sender, playerId, data, legacy ? "AutoSyncMap" : "MapAutoSync");
						Remind(sender, version, legacy);
					}
					else if (kind == 2 && !flag && !legacy)
					{
						ClientSync.Merged(data);
					}
				}
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"bad map package from {sender}: {arg}");
			}
		}

		private void Remind(long sender, string version, bool legacy)
		{
			if ((legacy || (System.Version.TryParse(version, out Version result) && result < new Version("0.3.0"))) && reminded.Add(sender))
			{
				string text = (legacy ? "Your map sync mod is out of date. Please replace AutoSyncMap with MapAutoSync 0.3.0 or later." : ("MapAutoSync " + version + " is out of date. This server runs 0.3.0, please update."));
				ZRoutedRpc.instance.InvokeRoutedRPC(sender, "ShowMessage", new object[2] { 2, text });
				string text2 = ZNet.instance.GetPeer(sender)?.m_playerName ?? sender.ToString();
				Log.LogInfo((object)(text2 + " runs " + (legacy ? "a version from before 0.3.0 (AutoSyncMap)" : version) + ": reminded to update"));
			}
		}

		private void ServerMerge(long sender, long playerId, byte[] upload, string rpc)
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			string worldName = ZNet.instance.GetWorldName();
			if (store == null || storeWorld != worldName)
			{
				string worldsSaveRootPath = SaveSystem.GetWorldsSaveRootPath(ZNet.m_world.m_fileSource);
				string text = Path.Combine(worldsSaveRootPath, worldName + ".mapautosync.dat");
				string text2 = Path.Combine(worldsSaveRootPath, worldName + ".autosyncmap.dat");
				if (!File.Exists(text) && File.Exists(text2))
				{
					File.Move(text2, text);
					Log.LogInfo((object)("took over the shared map from before 0.3.0: " + Path.GetFileName(text2) + " is now " + Path.GetFileName(text)));
				}
				store = new ServerStore(text);
				storeWorld = worldName;
			}
			ServerStore target = store;
			string who = ZNet.instance.GetPeer(sender)?.m_playerName ?? sender.ToString();
			Task.Run(delegate
			{
				try
				{
					Stopwatch watch = Stopwatch.StartNew();
					string info;
					byte[] merged = target.Merge(upload, playerId, out info);
					mainThread.Enqueue(delegate
					{
						if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.GetPeer(sender) != null)
						{
							Transfer.Send(sender, rpc, 2, 0L, merged);
						}
						Log.LogInfo((object)$"synced {who}: {upload.Length / 1024} KB up, {info}, {watch.ElapsedMilliseconds} ms");
					});
				}
				catch (Exception ex)
				{
					Exception ex2 = ex;
					Exception e = ex2;
					mainThread.Enqueue(delegate
					{
						Log.LogError((object)$"merge failed for {who}: {e}");
					});
				}
			});
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			MapToggle.Remove();
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}
	}
	internal static class MapBanner
	{
		private const float Seconds = 4f;

		private static TMP_Text s_text;

		private static float s_until;

		internal static void Show(string text, MessageType otherwise)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			if (!OnMap(text) && (Object)(object)Player.m_localPlayer != (Object)null)
			{
				((Character)Player.m_localPlayer).Message(otherwise, text, 0, (Sprite)null, false);
			}
		}

		private static bool OnMap(string text)
		{
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_largeRoot == (Object)null || !instance.m_largeRoot.activeInHierarchy || (Object)(object)instance.m_publicPosition == (Object)null)
			{
				return false;
			}
			if ((Object)(object)s_text == (Object)null)
			{
				TMP_Text componentInChildren = ((Component)instance.m_publicPosition).GetComponentInChildren<TMP_Text>(true);
				if ((Object)(object)componentInChildren == (Object)null)
				{
					return false;
				}
				s_text = Object.Instantiate<TMP_Text>(componentInChildren, instance.m_largeRoot.transform);
				((Object)s_text).name = "MapAutoSyncBanner";
				RectTransform rectTransform = s_text.rectTransform;
				Vector2 val = default(Vector2);
				((Vector2)(ref val))..ctor(0.5f, 1f);
				rectTransform.anchorMax = val;
				rectTransform.anchorMin = val;
				rectTransform.pivot = new Vector2(0.5f, 1f);
				rectTransform.anchoredPosition = new Vector2(0f, 0f - MapAutoSyncPlugin.BannerOffset.Value);
				rectTransform.sizeDelta = new Vector2(1200f, 80f);
				s_text.alignment = (TextAlignmentOptions)514;
				s_text.enableAutoSizing = false;
				s_text.fontSize = MapAutoSyncPlugin.BannerSize.Value;
				((Graphic)s_text).raycastTarget = false;
			}
			s_text.text = text;
			s_text.transform.SetAsLastSibling();
			((Component)s_text).gameObject.SetActive(true);
			s_until = Time.time + 4f;
			return true;
		}

		internal static void Frame()
		{
			if ((Object)(object)s_text != (Object)null && ((Component)s_text).gameObject.activeSelf && Time.time >= s_until)
			{
				((Component)s_text).gameObject.SetActive(false);
			}
		}

		internal static void Remove()
		{
			if ((Object)(object)s_text != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)s_text).gameObject);
				s_text = null;
			}
		}
	}
	internal static class MapToggle
	{
		private const float SyncNowTimeout = 10f;

		private const float ResultSeconds = 4f;

		private static Toggle s_toggle;

		private static Toggle s_syncNow;

		private static RectTransform s_toggleRow;

		private static RectTransform s_syncNowRow;

		private static float s_syncNowUntil;

		private static float s_labelResetAt = -1f;

		internal static void Ensure()
		{
			if ((Object)(object)Minimap.instance == (Object)null || (Object)(object)Minimap.instance.m_publicPosition == (Object)null)
			{
				return;
			}
			if ((Object)(object)s_toggle != (Object)null && (Object)(object)s_syncNow != (Object)null)
			{
				Place();
				if (s_syncNow.isOn && Time.time >= s_syncNowUntil)
				{
					Finish("No reply from the server");
				}
				else if (!s_syncNow.isOn && s_labelResetAt >= 0f && Time.time >= s_labelResetAt)
				{
					s_labelResetAt = -1f;
					SetLabel(((Component)s_syncNow).gameObject, MapAutoSyncPlugin.SyncNowLabel.Value);
				}
				return;
			}
			Remove();
			RectTransform val = SourceRow();
			s_toggle = Copy(val, "MapAutoSyncToggle", MapAutoSyncPlugin.ButtonLabel.Value, out s_toggleRow);
			s_toggle.isOn = MapAutoSyncPlugin.AutoSync.Value;
			((UnityEvent<bool>)(object)s_toggle.onValueChanged).AddListener((UnityAction<bool>)delegate(bool on)
			{
				MapAutoSyncPlugin.AutoSync.Value = on;
				if (on)
				{
					ClientSync.RequestNow();
				}
			});
			s_syncNow = Copy(val, "MapAutoSyncSyncNow", MapAutoSyncPlugin.SyncNowLabel.Value, out s_syncNowRow);
			s_syncNow.isOn = false;
			((UnityEvent<bool>)(object)s_syncNow.onValueChanged).AddListener((UnityAction<bool>)delegate(bool on)
			{
				if (on)
				{
					s_syncNowUntil = Time.time + 10f;
					s_labelResetAt = -1f;
					SetLabel(((Component)s_syncNow).gameObject, "Syncing...");
					Say("Syncing map...");
					ClientSync.RequestNow();
					ClientSync.Tick();
				}
			});
			if (InLayoutGroup((Transform)(object)val))
			{
				Transform val2 = (Transform)(object)val;
				GameObject sharedMapHint = Minimap.instance.m_sharedMapHint;
				if ((Object)(object)sharedMapHint != (Object)null && (Object)(object)sharedMapHint.transform.parent == (Object)(object)((Transform)val).parent && sharedMapHint.transform.GetSiblingIndex() < val2.GetSiblingIndex())
				{
					val2 = sharedMapHint.transform;
				}
				((Transform)s_toggleRow).SetSiblingIndex(val2.GetSiblingIndex());
				((Transform)s_syncNowRow).SetSiblingIndex(((Transform)s_toggleRow).GetSiblingIndex());
			}
			Place();
			MapAutoSyncPlugin.Log.LogInfo((object)$"added the Auto sync map tick-box and the Sync now button to the large map (copied {Path((Transform)(object)val)}, tick-box {Path(((Component)Minimap.instance.m_publicPosition).transform)}, layout group {InLayoutGroup((Transform)(object)val)})");
		}

		internal static bool SyncDone(bool changed)
		{
			if ((Object)(object)s_syncNow == (Object)null || !s_syncNow.isOn)
			{
				return false;
			}
			Finish(changed ? "Sync complete: new areas or pins" : "Sync complete", "Sync complete");
			return true;
		}

		private static void Finish(string message, string label = null)
		{
			s_syncNow.SetIsOnWithoutNotify(false);
			SetLabel(((Component)s_syncNow).gameObject, label ?? message);
			s_labelResetAt = Time.time + 4f;
			Say(message);
		}

		private static void Say(string message)
		{
			MapBanner.Show(message, (MessageType)2);
		}

		private static RectTransform SourceRow()
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			Transform transform = ((Component)Minimap.instance.m_publicPosition).transform;
			Transform val = (((Object)(object)Minimap.instance.m_largeRoot != (Object)null) ? Minimap.instance.m_largeRoot.transform : null);
			GameObject sharedMapHint = Minimap.instance.m_sharedMapHint;
			Transform val2 = transform;
			while (true)
			{
				Transform parent = val2.parent;
				RectTransform val3 = (RectTransform)(object)((parent is RectTransform) ? parent : null);
				if (val3 == null || !((Object)(object)val3 != (Object)(object)val) || (!((Object)(object)sharedMapHint == (Object)null) && sharedMapHint.transform.IsChildOf((Transform)(object)val3)) || ((Component)val3).GetComponentsInChildren<Selectable>(true).Length != 1)
				{
					break;
				}
				val2 = (Transform)(object)val3;
			}
			return (RectTransform)val2;
		}

		private static bool InLayoutGroup(Transform row)
		{
			if ((Object)(object)row.parent != (Object)null)
			{
				return (Object)(object)((Component)row.parent).GetComponent<LayoutGroup>() != (Object)null;
			}
			return false;
		}

		private static Toggle Copy(RectTransform source, string name, string label, out RectTransform row)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			GameObject val = Object.Instantiate<GameObject>(((Component)source).gameObject, ((Transform)source).parent);
			((Object)val).name = name;
			row = (RectTransform)val.transform;
			Toggle componentInChildren = val.GetComponentInChildren<Toggle>(true);
			componentInChildren.onValueChanged = new ToggleEvent();
			SetLabel(((Component)componentInChildren).gameObject, label);
			return componentInChildren;
		}

		private static void Place()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: 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)
			//IL_00e5: 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_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: 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_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: 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_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = SourceRow();
			if (InLayoutGroup((Transform)(object)val) || (Object)(object)((Transform)val).parent == (Object)null)
			{
				return;
			}
			Rect rect = val.rect;
			float num;
			if (!(((Rect)(ref rect)).height > 1f))
			{
				num = 32f;
			}
			else
			{
				rect = val.rect;
				num = ((Rect)(ref rect)).height + 4f;
			}
			float num2 = num;
			int num3 = 1;
			GameObject sharedMapHint = Minimap.instance.m_sharedMapHint;
			if ((Object)(object)sharedMapHint != (Object)null)
			{
				Transform transform = sharedMapHint.transform;
				RectTransform val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null);
				if (val2 != null)
				{
					float num4 = ((Transform)val).parent.InverseTransformPoint(Centre(val2)).y - ((Transform)val).parent.InverseTransformPoint(Centre(val)).y;
					if (num4 > 1f)
					{
						num2 = num4;
					}
					if (sharedMapHint.activeSelf)
					{
						num3 = 2;
					}
				}
			}
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(MapAutoSyncPlugin.ButtonOffsetX.Value, MapAutoSyncPlugin.ButtonOffsetY.Value);
			s_toggleRow.anchoredPosition = val.anchoredPosition + val3 + new Vector2(0f, num2 * (float)num3);
			s_syncNowRow.anchoredPosition = val.anchoredPosition + val3 + new Vector2(0f, num2 * (float)(num3 + 1));
		}

		private static Vector3 Centre(RectTransform rect)
		{
			//IL_0010: 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)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			Vector3[] array = (Vector3[])(object)new Vector3[4];
			rect.GetWorldCorners(array);
			return (array[0] + array[2]) * 0.5f;
		}

		private static string Path(Transform transform)
		{
			List<string> list = new List<string>();
			Transform val = (((Object)(object)Minimap.instance.m_largeRoot != (Object)null) ? Minimap.instance.m_largeRoot.transform : null);
			Transform val2 = transform;
			while ((Object)(object)val2 != (Object)null && list.Count < 8)
			{
				list.Insert(0, ((Object)val2).name);
				if ((Object)(object)val2 == (Object)(object)val)
				{
					break;
				}
				val2 = val2.parent;
			}
			return string.Join("/", list);
		}

		private static void SetLabel(GameObject root, string text)
		{
			Component[] componentsInChildren = root.GetComponentsInChildren<Component>(true);
			foreach (Component val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null) && !(val is Toggle))
				{
					PropertyInfo property = ((object)val).GetType().GetProperty("text");
					if (property != null && property.PropertyType == typeof(string) && property.CanWrite)
					{
						property.SetValue(val, text, null);
					}
				}
			}
		}

		internal static void Remove()
		{
			if ((Object)(object)s_toggleRow != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)s_toggleRow).gameObject);
			}
			if ((Object)(object)s_syncNowRow != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)s_syncNowRow).gameObject);
			}
			MapBanner.Remove();
			s_toggle = null;
			s_syncNow = null;
			s_toggleRow = null;
			s_syncNowRow = null;
		}
	}
	internal class ServerStore
	{
		private struct Pin
		{
			public long Owner;

			public string Name;

			public string Author;

			public float X;

			public float Y;

			public float Z;

			public int Type;

			public bool Checked;
		}

		private const int Version = 3;

		private readonly object m_lock = new object();

		private readonly string m_file;

		private byte[] m_explored;

		private List<Pin> m_pins = new List<Pin>();

		private bool m_loaded;

		internal ServerStore(string file)
		{
			m_file = file;
		}

		internal byte[] Merge(byte[] uploadCompressed, long playerId, out string info)
		{
			lock (m_lock)
			{
				Load();
				Parse(Utils.Decompress(uploadCompressed), out var explored, out var pins);
				int num = 0;
				if (m_explored == null || m_explored.Length != explored.Length)
				{
					m_explored = explored;
					byte[] array = explored;
					foreach (byte b in array)
					{
						num += b;
					}
				}
				else
				{
					for (int j = 0; j < explored.Length; j++)
					{
						if (explored[j] != 0 && m_explored[j] == 0)
						{
							m_explored[j] = 1;
							num++;
						}
					}
				}
				int count = m_pins.Count;
				m_pins.RemoveAll((Pin p) => p.Owner == playerId);
				int count2 = m_pins.Count;
				int num2 = 0;
				foreach (Pin item in pins)
				{
					if (item.Owner == playerId)
					{
						m_pins.Add(item);
						num2++;
					}
				}
				byte[] array2 = Utils.Compress(Write());
				string text = m_file + ".tmp";
				File.WriteAllBytes(text, array2);
				if (File.Exists(m_file))
				{
					File.Delete(m_file);
				}
				File.Move(text, m_file);
				info = $"{num} newly explored cells, {num2} own pins ({count - count2} before), {m_pins.Count} pins in total, {array2.Length / 1024} KB";
				return array2;
			}
		}

		private void Load()
		{
			if (!m_loaded)
			{
				m_loaded = true;
				if (File.Exists(m_file))
				{
					Parse(Utils.Decompress(File.ReadAllBytes(m_file)), out m_explored, out m_pins);
				}
			}
		}

		private static void Parse(byte[] data, out byte[] explored, out List<Pin> pins)
		{
			using BinaryReader binaryReader = new BinaryReader(new MemoryStream(data));
			int num = binaryReader.ReadInt32();
			if (num != 3)
			{
				throw new InvalidDataException($"shared map version {num}, expected {3}");
			}
			int num2 = binaryReader.ReadInt32();
			if (num2 < 0 || num2 > data.Length)
			{
				throw new InvalidDataException($"explored length {num2}");
			}
			explored = binaryReader.ReadBytes(num2);
			int num3 = binaryReader.ReadInt32();
			if (num3 < 0 || num3 > 100000)
			{
				throw new InvalidDataException($"pin count {num3}");
			}
			pins = new List<Pin>(num3);
			for (int i = 0; i < num3; i++)
			{
				pins.Add(new Pin
				{
					Owner = binaryReader.ReadInt64(),
					Name = binaryReader.ReadString(),
					X = binaryReader.ReadSingle(),
					Y = binaryReader.ReadSingle(),
					Z = binaryReader.ReadSingle(),
					Type = binaryReader.ReadInt32(),
					Checked = binaryReader.ReadBoolean(),
					Author = binaryReader.ReadString()
				});
			}
		}

		private byte[] Write()
		{
			using MemoryStream memoryStream = new MemoryStream(m_explored.Length + 64 * m_pins.Count + 16);
			using BinaryWriter binaryWriter = new BinaryWriter(memoryStream);
			binaryWriter.Write(3);
			binaryWriter.Write(m_explored.Length);
			binaryWriter.Write(m_explored);
			binaryWriter.Write(m_pins.Count);
			foreach (Pin pin in m_pins)
			{
				binaryWriter.Write(pin.Owner);
				binaryWriter.Write(pin.Name ?? "");
				binaryWriter.Write(pin.X);
				binaryWriter.Write(pin.Y);
				binaryWriter.Write(pin.Z);
				binaryWriter.Write(pin.Type);
				binaryWriter.Write(pin.Checked);
				binaryWriter.Write(pin.Author ?? "");
			}
			binaryWriter.Flush();
			return memoryStream.ToArray();
		}
	}
	internal static class Transfer
	{
		private class Incoming
		{
			public byte[][] Chunks;

			public int Received;

			public float Started;
		}

		internal const string Rpc = "MapAutoSync";

		internal const string LegacyRpc = "AutoSyncMap";

		internal const byte Upload = 1;

		internal const byte Merged = 2;

		private const int ChunkSize = 65536;

		private const float StaleSeconds = 120f;

		private static readonly Dictionary<(long, int), Incoming> s_incoming = new Dictionary<(long, int), Incoming>();

		private static int s_nextId = 1;

		internal static void Send(long target, string rpc, byte kind, long playerId, byte[] data)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			int num = s_nextId++;
			int num2 = Mathf.Max(1, (data.Length + 65536 - 1) / 65536);
			for (int i = 0; i < num2; i++)
			{
				int num3 = i * 65536;
				int num4 = Mathf.Min(65536, data.Length - num3);
				byte[] array = new byte[num4];
				Buffer.BlockCopy(data, num3, array, 0, num4);
				ZPackage val = new ZPackage();
				val.Write(kind);
				val.Write(num);
				val.Write(i);
				val.Write(num2);
				val.Write(playerId);
				if (rpc != "AutoSyncMap")
				{
					val.Write("0.3.0");
				}
				val.Write(array);
				ZRoutedRpc.instance.InvokeRoutedRPC(target, rpc, new object[1] { val });
			}
		}

		internal static bool Receive(long sender, ZPackage pkg, bool legacy, out byte kind, out long playerId, out string version, out byte[] data)
		{
			kind = pkg.ReadByte();
			int item = pkg.ReadInt();
			int num = pkg.ReadInt();
			int num2 = pkg.ReadInt();
			playerId = pkg.ReadLong();
			version = (legacy ? "" : pkg.ReadString());
			byte[] array = pkg.ReadByteArray();
			data = null;
			if (num2 < 1 || num2 > 4096 || num < 0 || num >= num2)
			{
				return false;
			}
			foreach (var item2 in (from kv in s_incoming
				where Time.time - kv.Value.Started > 120f
				select kv.Key).ToList())
			{
				s_incoming.Remove(item2);
			}
			(long, int) key = (sender, item);
			if (!s_incoming.TryGetValue(key, out var value))
			{
				Dictionary<(long, int), Incoming> dictionary = s_incoming;
				Incoming obj = new Incoming
				{
					Chunks = new byte[num2][],
					Started = Time.time
				};
				value = obj;
				dictionary[key] = obj;
			}
			if (value.Chunks.Length != num2 || value.Chunks[num] != null)
			{
				return false;
			}
			value.Chunks[num] = array;
			if (++value.Received < num2)
			{
				return false;
			}
			s_incoming.Remove(key);
			data = new byte[value.Chunks.Sum((byte[] c) => c.Length)];
			int num3 = 0;
			byte[][] chunks = value.Chunks;
			foreach (byte[] array2 in chunks)
			{
				Buffer.BlockCopy(array2, 0, data, num3, array2.Length);
				num3 += array2.Length;
			}
			return true;
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}