Decompiled source of HandDrawnMap v0.4.1

Hand_Drawn_Map.dll

Decompiled 5 days ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Hand_Drawn_Map")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Local hand-drawn cartography canvas for Valheim.")]
[assembly: AssemblyFileVersion("0.4.1.0")]
[assembly: AssemblyInformationalVersion("0.4.1+c3bc977be77a30366a7e8eac4310f866bcdb5f9d")]
[assembly: AssemblyProduct("Hand_Drawn_Map")]
[assembly: AssemblyTitle("Hand_Drawn_Map")]
[assembly: AssemblyVersion("0.4.1.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 Hand_Drawn_Map
{
	public sealed class BrushGraphic : MaskableGraphic
	{
		protected override void OnPopulateMesh(VertexHelper mesh)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: 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_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			mesh.Clear();
			Rect rect = ((Graphic)this).rectTransform.rect;
			float num = ((Rect)(ref rect)).width * 0.5f;
			float num2 = Mathf.Max(0f, num - 1f);
			Vector2 val = default(Vector2);
			Vector2 val2 = default(Vector2);
			Color color = default(Color);
			for (int i = 0; i < 48; i++)
			{
				float num3 = (float)i * MathF.PI * 2f / 48f;
				float num4 = (float)(i + 1) * MathF.PI * 2f / 48f;
				((Vector2)(ref val))..ctor(Mathf.Cos(num3), Mathf.Sin(num3));
				((Vector2)(ref val2))..ctor(Mathf.Cos(num4), Mathf.Sin(num4));
				int currentVertCount = mesh.currentVertCount;
				Add(mesh, Vector2.zero, ((Graphic)this).color);
				Add(mesh, val * num2, ((Graphic)this).color);
				Add(mesh, val2 * num2, ((Graphic)this).color);
				mesh.AddTriangle(currentVertCount, currentVertCount + 1, currentVertCount + 2);
				((Color)(ref color))..ctor(((Graphic)this).color.r, ((Graphic)this).color.g, ((Graphic)this).color.b, Mathf.Min(1f, ((Graphic)this).color.a * 3f));
				currentVertCount = mesh.currentVertCount;
				Add(mesh, val * num2, color);
				Add(mesh, val * num, color);
				Add(mesh, val2 * num, color);
				Add(mesh, val2 * num2, color);
				mesh.AddTriangle(currentVertCount, currentVertCount + 1, currentVertCount + 2);
				mesh.AddTriangle(currentVertCount, currentVertCount + 2, currentVertCount + 3);
			}
		}

		private static void Add(VertexHelper mesh, Vector2 position, Color color)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			UIVertex simpleVert = UIVertex.simpleVert;
			simpleVert.position = Vector2.op_Implicit(position);
			simpleVert.color = Color32.op_Implicit(color);
			simpleVert.uv0 = Vector4.op_Implicit(Vector2.zero);
			mesh.AddVert(simpleVert);
		}
	}
	internal sealed class CanvasWindow : IDisposable
	{
		private enum Tool
		{
			Pencil,
			Eraser,
			Annotate
		}

		private readonly MapPlugin plugin;

		private readonly GameObject root;

		private readonly Canvas canvas;

		private readonly RectTransform panel;

		private readonly RectTransform viewport;

		private readonly Text status;

		private readonly Text pencilLabel;

		private readonly Text eraserLabel;

		private readonly Text annotateLabel;

		private readonly Text scaleLabel;

		private readonly RectTransform scaleBar;

		private readonly RectTransform scaleEnd;

		private readonly BrushGraphic brush;

		private readonly Image locationMarker;

		private Image nativeMarkerImage;

		private readonly Vector3[] nativeCorners = (Vector3[])(object)new Vector3[4];

		private readonly bool readOnly;

		private readonly List<ChartGraphic> batches = new List<ChartGraphic>();

		internal readonly List<InkSegment> Segments = new List<InkSegment>();

		private bool invalid = true;

		private Tool tool;

		private bool activeTemporary;

		private Guid gesture;

		private bool gestureSubmitted;

		private bool unsentPoints;

		private float nextStrokeSend;

		private float nextFadeRefresh;

		private bool panning;

		private bool erasing;

		private ChartStroke activeStroke;

		private ChartPoint lastBrush;

		private Vector2 lastPointer;

		private float eraseRadius;

		private Vector2 lastSize;

		private float lastProjection;

		private readonly Font font;

		private string notice;

		internal bool IsOpen
		{
			get
			{
				if ((Object)(object)root != (Object)null)
				{
					return root.activeSelf;
				}
				return false;
			}
		}

		internal bool InGesture
		{
			get
			{
				if (activeStroke == null && !erasing)
				{
					return panning;
				}
				return true;
			}
		}

		private ChartDocument Document => plugin.Document;

		private float Extent => (float)Minimap.instance.m_textureSize * Minimap.instance.m_pixelSize;

		private float WorldPerPixel
		{
			get
			{
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: 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)
				RectTransform rectTransform = ((Graphic)Minimap.instance.m_mapImageLarge).rectTransform;
				Rect rect = rectTransform.rect;
				float num = ((Rect)(ref rect)).height * Mathf.Abs(((Transform)rectTransform).lossyScale.y);
				if (num < 1f)
				{
					num = Screen.height;
				}
				return Extent * Document.Zoom * canvas.scaleFactor / num;
			}
		}

		internal CanvasWindow(MapPlugin plugin, bool readOnly)
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Expected O, but got Unknown
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_02db: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_0314: Unknown result type (might be due to invalid IL or missing references)
			//IL_031b: Unknown result type (might be due to invalid IL or missing references)
			//IL_039d: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_040d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0427: Unknown result type (might be due to invalid IL or missing references)
			//IL_0436: Unknown result type (might be due to invalid IL or missing references)
			//IL_044d: Unknown result type (might be due to invalid IL or missing references)
			//IL_045c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0487: Unknown result type (might be due to invalid IL or missing references)
			//IL_0497: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_04cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_05b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_05cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_05f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_05fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0619: Unknown result type (might be due to invalid IL or missing references)
			//IL_062e: Unknown result type (might be due to invalid IL or missing references)
			//IL_064d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0662: Unknown result type (might be due to invalid IL or missing references)
			//IL_0685: Unknown result type (might be due to invalid IL or missing references)
			//IL_068f: Expected O, but got Unknown
			CanvasWindow canvasWindow = this;
			this.plugin = plugin;
			this.readOnly = readOnly;
			font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
			root = new GameObject("HandDrawnMap", new Type[4]
			{
				typeof(RectTransform),
				typeof(Canvas),
				typeof(CanvasScaler),
				typeof(GraphicRaycaster)
			});
			Object.DontDestroyOnLoad((Object)(object)root);
			canvas = root.GetComponent<Canvas>();
			canvas.renderMode = (RenderMode)0;
			canvas.sortingOrder = 100;
			CanvasScaler component = root.GetComponent<CanvasScaler>();
			component.uiScaleMode = (ScaleMode)1;
			component.referenceResolution = new Vector2(1920f, 1080f);
			component.screenMatchMode = (ScreenMatchMode)0;
			component.matchWidthOrHeight = 0.5f;
			panel = Rect("MapPanel", root.transform);
			panel.anchorMin = Vector2.zero;
			panel.anchorMax = Vector2.one;
			AddImage(panel, new Color(0.12f, 0.1f, 0.08f));
			RectTransform val = Rect("Toolbar", (Transform)(object)panel);
			val.anchorMin = new Vector2(0f, 1f);
			val.anchorMax = Vector2.one;
			val.pivot = new Vector2(0.5f, 1f);
			val.sizeDelta = new Vector2(0f, 96f);
			Text val2 = Label(val, "HAND DRAWN MAP", new Vector2(20f, -26f), new Vector2(270f, 44f), 23);
			RectTransform rectTransform = ((Graphic)val2).rectTransform;
			RectTransform rectTransform2 = ((Graphic)val2).rectTransform;
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0f, 1f);
			rectTransform2.anchorMax = val3;
			rectTransform.anchorMin = val3;
			pencilLabel = Button(val, "Pencil", 300f, delegate
			{
				canvasWindow.SelectTool(Tool.Pencil);
			});
			eraserLabel = Button(val, "Eraser", 430f, delegate
			{
				canvasWindow.SelectTool(Tool.Eraser);
			});
			annotateLabel = Button(val, "Annotate", 560f, delegate
			{
				canvasWindow.SelectTool(Tool.Annotate);
			});
			Button(val, "Close", 690f, plugin.CloseMap);
			Text val4 = Label(val, "Clear", new Vector2(850f, -27f), new Vector2(65f, 38f), 20);
			RectTransform rectTransform3 = ((Graphic)val4).rectTransform;
			RectTransform rectTransform4 = ((Graphic)val4).rectTransform;
			((Vector2)(ref val3))..ctor(0f, 1f);
			rectTransform4.anchorMax = val3;
			rectTransform3.anchorMin = val3;
			Button(val, "Guides", 920f, ClearGuides);
			Button(val, "Annotations", 1050f, ClearAnnotations, 150f);
			Button(val, "Resync", 1230f, delegate
			{
				plugin.Shared.RequestResync();
			});
			scaleLabel = Label(val, "", new Vector2(300f, -59f), new Vector2(200f, 24f), 17);
			RectTransform rectTransform5 = ((Graphic)scaleLabel).rectTransform;
			RectTransform rectTransform6 = ((Graphic)scaleLabel).rectTransform;
			((Vector2)(ref val3))..ctor(0f, 1f);
			rectTransform6.anchorMax = val3;
			rectTransform5.anchorMin = val3;
			scaleBar = ScalePart(val, new Vector2(300f, -82f), new Vector2(180f, 2f));
			ScalePart(val, new Vector2(300f, -85f), new Vector2(2f, 8f));
			scaleEnd = ScalePart(val, new Vector2(478f, -85f), new Vector2(2f, 8f));
			viewport = Rect("Parchment", (Transform)(object)panel);
			viewport.anchorMin = Vector2.zero;
			viewport.anchorMax = Vector2.one;
			viewport.offsetMin = new Vector2(12f, 44f);
			viewport.offsetMax = new Vector2(-12f, -100f);
			AddImage(viewport, new Color(0.86f, 0.81f, 0.68f));
			((Component)viewport).gameObject.AddComponent<RectMask2D>();
			RectTransform val5 = Rect("PlayerLocation", (Transform)(object)viewport);
			locationMarker = ((Component)val5).gameObject.AddComponent<Image>();
			((Graphic)locationMarker).raycastTarget = false;
			locationMarker.preserveAspect = true;
			((Component)val5).gameObject.SetActive(false);
			RectTransform val6 = Rect("BrushFootprint", (Transform)(object)viewport);
			brush = ((Component)val6).gameObject.AddComponent<BrushGraphic>();
			((Graphic)brush).raycastTarget = false;
			((Component)brush).gameObject.SetActive(false);
			RectTransform val7 = Rect("Status", (Transform)(object)panel);
			val7.anchorMin = Vector2.zero;
			val7.anchorMax = new Vector2(1f, 0f);
			val7.pivot = new Vector2(0.5f, 0f);
			val7.sizeDelta = new Vector2(-24f, 40f);
			status = Label(val7, "", Vector2.zero, Vector2.zero, 18);
			((Graphic)status).rectTransform.anchorMin = Vector2.zero;
			((Graphic)status).rectTransform.anchorMax = Vector2.one;
			((Graphic)status).rectTransform.offsetMin = new Vector2(8f, 0f);
			((Graphic)status).rectTransform.offsetMax = Vector2.zero;
			UpdateTools();
			root.SetActive(false);
			Canvas.willRenderCanvases += new WillRenderCanvases(UpdateLocationMarker);
		}

		internal void SetOpen(bool open)
		{
			FinishGesture();
			root.SetActive(open);
			if (open)
			{
				RaiseCanvas();
				Canvas.ForceUpdateCanvases();
				MatchNativeBounds();
				Canvas.ForceUpdateCanvases();
				invalid = true;
			}
			else
			{
				((Component)brush).gameObject.SetActive(false);
				((Component)locationMarker).gameObject.SetActive(false);
			}
		}

		internal void Tick()
		{
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02db: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_031c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0368: Unknown result type (might be due to invalid IL or missing references)
			if (ZInput.GetKeyDown((KeyCode)27, true))
			{
				plugin.CloseMap();
				return;
			}
			ZCursor.LockState = (CursorLockMode)0;
			ZCursor.Show();
			MatchNativeBounds();
			if (!readOnly && plugin.Shared.CanEdit && Input.GetKeyDown((KeyCode)122) && (Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305)))
			{
				FinishGesture();
				notice = (plugin.Shared.Undo() ? null : plugin.Shared.Status);
			}
			Vector2 val = Vector2.op_Implicit(ZInput.pointerPosition);
			bool flag = RectTransformUtility.RectangleContainsScreenPoint(viewport, val, (Camera)null);
			Vector2 val2 = default(Vector2);
			RectTransformUtility.ScreenPointToLocalPointInRectangle(viewport, val, (Camera)null, ref val2);
			ChartPoint world = ToWorld(val2);
			if (flag && !InGesture)
			{
				float mouseScrollWheel = ZInput.GetMouseScrollWheel();
				if (mouseScrollWheel != 0f)
				{
					Zoom(Mathf.Clamp(mouseScrollWheel, -0.05f, 0.05f) * 2f, val2);
				}
			}
			if (flag && !InGesture && ZInput.GetMouseButtonDown(2))
			{
				panning = true;
				lastPointer = val2;
			}
			if (panning)
			{
				if (!ZInput.GetMouseButton(2))
				{
					panning = false;
				}
				else
				{
					Vector2 val3 = val2 - lastPointer;
					Document.Center -= new ChartPoint(val3.x, val3.y) * WorldPerPixel;
					ClampCenter();
					lastPointer = val2;
					Changed();
				}
			}
			if (flag && !InGesture && !readOnly && plugin.Shared.CanEdit && ZInput.GetMouseButtonDown(0))
			{
				gesture = Guid.NewGuid();
				gestureSubmitted = false;
				if (tool == Tool.Eraser)
				{
					erasing = true;
					lastBrush = world;
					eraseRadius = plugin.EraserPixels.Value * WorldPerPixel;
					EraseTo(world, initial: true);
				}
				else
				{
					StartStroke(world);
				}
			}
			if ((plugin.Shared.Annotations.Count > 0 || plugin.Guides.Count > 0) && Time.unscaledTime >= nextFadeRefresh)
			{
				invalid = true;
				nextFadeRefresh = Time.unscaledTime + 0.05f;
			}
			if (activeStroke != null || erasing)
			{
				if (!ZInput.GetMouseButton(0) || !flag || !plugin.Shared.CanEdit)
				{
					FinishGesture();
				}
				else if (erasing)
				{
					EraseTo(world);
				}
				else
				{
					AppendPoint(world);
				}
			}
			if (activeStroke != null && Time.unscaledTime >= nextStrokeSend)
			{
				FlushStroke();
			}
			Rect rect = viewport.rect;
			if (((Rect)(ref rect)).size != lastSize || !Mathf.Approximately(lastProjection, WorldPerPixel))
			{
				invalid = true;
			}
			if (invalid)
			{
				Rebuild();
			}
			UpdateScale();
			UpdateBrush(val2, flag);
			string text = "Right-click: survey with compass";
			status.text = (readOnly ? "Chart could not be loaded. Saving and editing are disabled; check the log and restore the .bak file." : (notice ?? $"LMB: {tool}   |   Middle drag: pan   |   Wheel: zoom   |   Ctrl+Z: undo own edits   |   {plugin.MapKey.Value} / Esc: close   |   {text}   |   {plugin.Shared.Status}"));
		}

		private void StartStroke(ChartPoint world)
		{
			activeTemporary = tool == Tool.Annotate;
			activeStroke = new ChartStroke(plugin.PencilPixels.Value * WorldPerPixel);
			notice = null;
			activeStroke.Points.Add(world);
			unsentPoints = true;
			nextStrokeSend = Time.unscaledTime + 0.05f;
			invalid = true;
		}

		private void AppendPoint(ChartPoint world)
		{
			if (!((world - activeStroke.Points[activeStroke.Points.Count - 1]).LengthSquared < WorldPerPixel * WorldPerPixel))
			{
				if (activeStroke.Points.Count >= 128)
				{
					FlushStroke();
				}
				if (activeStroke != null)
				{
					activeStroke.Points.Add(world);
					unsentPoints = true;
					invalid = true;
				}
			}
		}

		private void EraseTo(ChartPoint world, bool initial = false)
		{
			if (initial || !((world - lastBrush).LengthSquared < 1E-06f))
			{
				gestureSubmitted |= plugin.Shared.Erase(gesture, lastBrush, world, eraseRadius);
				invalid = true;
				lastBrush = world;
			}
		}

		private void FlushStroke()
		{
			nextStrokeSend = Time.unscaledTime + 0.05f;
			if (activeStroke == null || !unsentPoints)
			{
				return;
			}
			ChartPoint item = activeStroke.Points[activeStroke.Points.Count - 1];
			if (plugin.Shared.Draw(gesture, activeStroke.Points.ToArray(), activeStroke.Width, activeTemporary))
			{
				gestureSubmitted = true;
				activeStroke.Points.Clear();
				activeStroke.Points.Add(item);
			}
			else
			{
				if (!activeTemporary)
				{
					plugin.Shared.PreserveDraft(gesture, activeStroke.Points.ToArray(), activeStroke.Width);
				}
				notice = plugin.Shared.Status;
				activeStroke = null;
			}
			unsentPoints = false;
			invalid = true;
		}

		private void FinishGesture()
		{
			FlushStroke();
			if (gestureSubmitted && plugin.Shared.CanEdit)
			{
				plugin.Shared.End(gesture);
			}
			gestureSubmitted = false;
			gesture = Guid.Empty;
			activeStroke = null;
			unsentPoints = false;
			erasing = false;
			panning = false;
			invalid = true;
		}

		internal void FinishSharedGesture()
		{
			FinishGesture();
		}

		internal void Invalidate()
		{
			invalid = true;
		}

		private void Changed()
		{
			plugin.Dirty = true;
			invalid = true;
		}

		private void Zoom(float step, Vector2 local)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			ChartPoint chartPoint = ToWorld(local);
			Document.Zoom = Mathf.Clamp(Document.Zoom * (1f - step), Minimap.instance.m_minZoom, Minimap.instance.m_maxZoom);
			Document.Center += chartPoint - ToWorld(local);
			ClampCenter();
			Changed();
		}

		private void ClampCenter()
		{
			float num = Extent * 0.5f;
			Document.Center = new ChartPoint(Mathf.Clamp(Document.Center.X, 0f - num, num), Mathf.Clamp(Document.Center.Z, 0f - num, num));
		}

		private ChartPoint ToWorld(Vector2 point)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			return ChartGeometry.ToWorld(new ChartPoint(point.x, point.y), Document.Center, WorldPerPixel);
		}

		private ChartPoint ToScreen(ChartPoint point)
		{
			return ChartGeometry.ToScreen(point, Document.Center, WorldPerPixel);
		}

		private void Rebuild()
		{
			//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)
			//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)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0264: Unknown result type (might be due to invalid IL or missing references)
			//IL_026f: Unknown result type (might be due to invalid IL or missing references)
			//IL_027b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0280: Unknown result type (might be due to invalid IL or missing references)
			//IL_0281: Unknown result type (might be due to invalid IL or missing references)
			//IL_0288: Unknown result type (might be due to invalid IL or missing references)
			Segments.Clear();
			Rect rect = viewport.rect;
			lastSize = ((Rect)(ref rect)).size;
			lastProjection = WorldPerPixel;
			Color val = default(Color);
			((Color)(ref val))..ctor(0.18f, 0.15f, 0.12f);
			foreach (ChartStroke stroke in Document.Strokes)
			{
				RenderStroke(stroke, val);
			}
			foreach (TemporaryInk annotation in plugin.Shared.Annotations)
			{
				RenderStroke(annotation.Stroke, new Color(0.38f, 0.22f, 0.12f, annotation.Opacity * 0.8f));
			}
			if (activeStroke != null)
			{
				RenderStroke(activeStroke, (Color)(activeTemporary ? new Color(0.38f, 0.22f, 0.12f, 0.8f) : val));
			}
			foreach (SurveyGuide guide in plugin.Guides)
			{
				float num = Mathf.Clamp01((guide.Expires - Time.unscaledTime) / Mathf.Min(30f, guide.Expires - guide.Created));
				ChartPoint chartPoint = guide.Origin + guide.Direction * ChartPoint.Dot(Document.Center - guide.Origin, guide.Direction);
				float num2 = ((Vector2)(ref lastSize)).magnitude * lastProjection;
				AddSegment(chartPoint - guide.Direction * num2, chartPoint + guide.Direction * num2, 1.5f, new Color(0.16f, 0.35f, 0.48f, num * 0.65f));
			}
			int num3 = Mathf.Max(1, (Segments.Count + 6000 - 1) / 6000);
			while (batches.Count < num3)
			{
				RectTransform obj = Rect("Ink", (Transform)(object)viewport);
				obj.anchorMin = Vector2.zero;
				obj.anchorMax = Vector2.one;
				Vector2 offsetMin = (obj.offsetMax = Vector2.zero);
				obj.offsetMin = offsetMin;
				ChartGraphic chartGraphic = ((Component)obj).gameObject.AddComponent<ChartGraphic>();
				((Graphic)chartGraphic).raycastTarget = false;
				chartGraphic.Window = this;
				chartGraphic.Batch = batches.Count;
				batches.Add(chartGraphic);
			}
			for (int i = 0; i < batches.Count; i++)
			{
				((Component)batches[i]).gameObject.SetActive(i < num3);
				if (i < num3)
				{
					((Graphic)batches[i]).SetVerticesDirty();
				}
			}
			((Component)locationMarker).transform.SetAsLastSibling();
			((Component)brush).transform.SetAsLastSibling();
			invalid = false;
		}

		private void RenderStroke(ChartStroke stroke, Color color)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			if (stroke.Points.Count == 1)
			{
				AddSegment(stroke.Points[0], stroke.Points[0], stroke.Width / lastProjection, color);
			}
			for (int i = 1; i < stroke.Points.Count; i++)
			{
				AddSegment(stroke.Points[i - 1], stroke.Points[i], stroke.Width / lastProjection, color);
			}
		}

		private void AddSegment(ChartPoint a, ChartPoint b, float width, Color color)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: 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)
			a = ToScreen(a);
			b = ToScreen(b);
			if (ChartGeometry.ClipLine(ref a, ref b, lastSize.x * 0.5f + width, lastSize.y * 0.5f + width))
			{
				Segments.Add(new InkSegment(new Vector2(a.X, a.Z), new Vector2(b.X, b.Z), Mathf.Max(0.5f, width), color));
			}
		}

		private void UpdateTools()
		{
			//IL_002b: 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)
			//IL_0047: 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_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			Color val = default(Color);
			((Color)(ref val))..ctor(1f, 0.75f, 0.3f);
			((Graphic)pencilLabel).color = ((tool == Tool.Pencil) ? val : Color.white);
			((Graphic)eraserLabel).color = ((tool == Tool.Eraser) ? val : Color.white);
			((Graphic)annotateLabel).color = ((tool == Tool.Annotate) ? val : Color.white);
		}

		private void SelectTool(Tool selected)
		{
			FinishGesture();
			tool = selected;
			notice = null;
			UpdateTools();
		}

		private void ClearGuides()
		{
			plugin.Guides.Clear();
			notice = null;
			invalid = true;
		}

		private void ClearAnnotations()
		{
			FinishGesture();
			plugin.Shared.ClearAnnotations();
			notice = null;
			invalid = true;
		}

		private void UpdateScale()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			float num = MetricScale.Length(WorldPerPixel, 180f);
			float num2 = num / WorldPerPixel;
			scaleBar.sizeDelta = new Vector2(num2, 2f);
			scaleEnd.anchoredPosition = new Vector2(300f + num2 - 2f, -85f);
			scaleLabel.text = MetricScale.Label(num);
		}

		private void UpdateBrush(Vector2 local, bool inside)
		{
			//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_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: 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_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			((Component)brush).gameObject.SetActive(inside && !panning && !readOnly && plugin.Shared.CanEdit);
			if (inside)
			{
				float num = ((tool != Tool.Eraser) ? (((activeStroke != null) ? (activeStroke.Width / WorldPerPixel) : plugin.PencilPixels.Value) * 0.5f) : (erasing ? (eraseRadius / WorldPerPixel) : plugin.EraserPixels.Value));
				((Graphic)brush).rectTransform.anchoredPosition = local;
				((Graphic)brush).rectTransform.sizeDelta = Vector2.one * Mathf.Max(1f, num * 2f);
				Color color = ((tool == Tool.Eraser) ? new Color(0.65f, 0.15f, 0.12f) : new Color(0.12f, 0.22f, 0.3f));
				color.a = plugin.BrushOpacity.Value;
				((Graphic)brush).color = color;
			}
		}

		private void UpdateLocationMarker()
		{
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: 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_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			if (!IsOpen)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if (!plugin.ShowMyLocation.Value || (Object)(object)localPlayer == (Object)null || (Object)(object)Minimap.instance == (Object)null)
			{
				((Component)locationMarker).gameObject.SetActive(false);
				return;
			}
			if ((Object)(object)nativeMarkerImage == (Object)null)
			{
				nativeMarkerImage = ((Component)Minimap.instance.m_largeMarker).GetComponentInChildren<Image>(true);
			}
			if ((Object)(object)nativeMarkerImage == (Object)null)
			{
				((Component)locationMarker).gameObject.SetActive(false);
				return;
			}
			Vector3 position = ((Component)localPlayer).transform.position;
			ChartPoint chartPoint = ToScreen(new ChartPoint(position.x, position.z));
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(chartPoint.X, chartPoint.Z);
			Rect rect = viewport.rect;
			if (!((Rect)(ref rect)).Contains(val))
			{
				((Component)locationMarker).gameObject.SetActive(false);
				return;
			}
			locationMarker.sprite = nativeMarkerImage.overrideSprite;
			((Graphic)locationMarker).color = ((Graphic)nativeMarkerImage).color;
			RectTransform rectTransform = ((Graphic)nativeMarkerImage).rectTransform;
			RectTransform rectTransform2 = ((Graphic)locationMarker).rectTransform;
			rect = rectTransform.rect;
			float num = ((Rect)(ref rect)).width * Mathf.Abs(((Transform)rectTransform).lossyScale.x);
			rect = rectTransform.rect;
			rectTransform2.sizeDelta = new Vector2(num, ((Rect)(ref rect)).height * Mathf.Abs(((Transform)rectTransform).lossyScale.y)) / canvas.scaleFactor;
			((Graphic)locationMarker).rectTransform.anchoredPosition = val;
			Camera mainCamera = Utils.GetMainCamera();
			float num2 = (((Object)(object)mainCamera != (Object)null) ? ((Component)mainCamera).transform.eulerAngles.y : ((Component)localPlayer).transform.eulerAngles.y);
			((Transform)((Graphic)locationMarker).rectTransform).localRotation = Quaternion.Euler(0f, 0f, 0f - num2);
			((Component)locationMarker).gameObject.SetActive(true);
		}

		private void RaiseCanvas()
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			int sortingLayerID = canvas.sortingLayerID;
			int num = 100;
			int num2 = SortingLayer.GetLayerValueFromID(sortingLayerID);
			Canvas[] array = Resources.FindObjectsOfTypeAll<Canvas>();
			foreach (Canvas val in array)
			{
				if ((Object)(object)val == (Object)(object)canvas || !((Behaviour)val).isActiveAndEnabled)
				{
					continue;
				}
				Scene scene = ((Component)val).gameObject.scene;
				if (((Scene)(ref scene)).IsValid())
				{
					int layerValueFromID = SortingLayer.GetLayerValueFromID(val.sortingLayerID);
					if (layerValueFromID > num2)
					{
						sortingLayerID = val.sortingLayerID;
						num2 = layerValueFromID;
						num = val.sortingOrder;
					}
					else if (layerValueFromID == num2)
					{
						num = Mathf.Max(num, val.sortingOrder);
					}
				}
			}
			canvas.sortingLayerID = sortingLayerID;
			canvas.overrideSorting = true;
			canvas.sortingOrder = Mathf.Min(32767, num + 1);
		}

		private void MatchNativeBounds()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: 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_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			RectTransform rectTransform = ((Graphic)Minimap.instance.m_mapImageLarge).rectTransform;
			rectTransform.GetWorldCorners(nativeCorners);
			Canvas componentInParent = ((Component)rectTransform).GetComponentInParent<Canvas>();
			Camera obj = (((Object)(object)componentInParent != (Object)null && (int)componentInParent.renderMode != 0) ? componentInParent.worldCamera : null);
			Vector2 val = RectTransformUtility.WorldToScreenPoint(obj, nativeCorners[0]);
			Vector2 val2 = RectTransformUtility.WorldToScreenPoint(obj, nativeCorners[2]);
			RectTransform val3 = (RectTransform)root.transform;
			Vector2 val4 = default(Vector2);
			RectTransformUtility.ScreenPointToLocalPointInRectangle(val3, val, (Camera)null, ref val4);
			Vector2 val5 = default(Vector2);
			RectTransformUtility.ScreenPointToLocalPointInRectangle(val3, val2, (Camera)null, ref val5);
			Vector2 val6 = val4;
			Rect rect = val3.rect;
			Vector2 val7 = val6 - ((Rect)(ref rect)).min;
			Vector2 val8 = val5;
			rect = val3.rect;
			Vector2 val9 = val8 - ((Rect)(ref rect)).max;
			if (val2.x - val.x < (float)Screen.width * 0.25f || val2.y - val.y < (float)Screen.height * 0.25f)
			{
				((Vector2)(ref val7))..ctor(100f, 100f);
				((Vector2)(ref val9))..ctor(-100f, -100f);
			}
			if (panel.offsetMin != val7 || panel.offsetMax != val9)
			{
				panel.offsetMin = val7;
				panel.offsetMax = val9;
				invalid = true;
			}
		}

		private static RectTransform ScalePart(RectTransform parent, Vector2 position, Vector2 size)
		{
			//IL_001e: 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)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			RectTransform obj = Rect("Scale", (Transform)(object)parent);
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0f, 1f);
			obj.anchorMax = val;
			obj.anchorMin = val;
			obj.pivot = Vector2.zero;
			obj.anchoredPosition = position;
			obj.sizeDelta = size;
			AddImage(obj, new Color(0.9f, 0.86f, 0.75f));
			((Graphic)((Component)obj).GetComponent<Image>()).raycastTarget = false;
			return obj;
		}

		private static RectTransform Rect(string name, Transform parent)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			RectTransform component = new GameObject(name, new Type[1] { typeof(RectTransform) }).GetComponent<RectTransform>();
			((Transform)component).SetParent(parent, false);
			return component;
		}

		private static void AddImage(RectTransform rect, Color color)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			((Graphic)((Component)rect).gameObject.AddComponent<Image>()).color = color;
		}

		private Text Label(RectTransform parent, string text, Vector2 position, Vector2 size, int fontSize)
		{
			//IL_001e: 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)
			//IL_0035: 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_0047: 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)
			RectTransform obj = Rect("Label", (Transform)(object)parent);
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0f, 0.5f);
			obj.anchorMax = val;
			obj.anchorMin = val;
			obj.pivot = new Vector2(0f, 0.5f);
			obj.anchoredPosition = position;
			obj.sizeDelta = size;
			Text obj2 = ((Component)obj).gameObject.AddComponent<Text>();
			obj2.font = font;
			obj2.fontSize = fontSize;
			((Graphic)obj2).color = Color.white;
			obj2.text = text;
			obj2.alignment = (TextAnchor)3;
			((Graphic)obj2).raycastTarget = false;
			return obj2;
		}

		private Text Button(RectTransform parent, string caption, float x, Action action, float width = 120f)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: 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_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: 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_00b4: Expected O, but got Unknown
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = Rect(caption, (Transform)(object)parent);
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0f, 1f);
			val.anchorMax = val2;
			val.anchorMin = val2;
			val.pivot = new Vector2(0f, 1f);
			val.anchoredPosition = new Vector2(x, -8f);
			val.sizeDelta = new Vector2(width, 38f);
			AddImage(val, new Color(0.25f, 0.21f, 0.16f));
			Button obj = ((Component)val).gameObject.AddComponent<Button>();
			((Selectable)obj).targetGraphic = (Graphic)(object)((Component)val).GetComponent<Image>();
			((UnityEvent)obj.onClick).AddListener((UnityAction)delegate
			{
				action();
			});
			return Label(val, caption, new Vector2(12f, 0f), new Vector2(width - 24f, 38f), 20);
		}

		public void Dispose()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			Canvas.willRenderCanvases -= new WillRenderCanvases(UpdateLocationMarker);
			if ((Object)(object)root != (Object)null)
			{
				Object.Destroy((Object)(object)root);
			}
		}
	}
	public readonly struct ChartPoint
	{
		public readonly float X;

		public readonly float Z;

		public float LengthSquared => X * X + Z * Z;

		public ChartPoint(float x, float z)
		{
			X = x;
			Z = z;
		}

		public static ChartPoint operator +(ChartPoint a, ChartPoint b)
		{
			return new ChartPoint(a.X + b.X, a.Z + b.Z);
		}

		public static ChartPoint operator -(ChartPoint a, ChartPoint b)
		{
			return new ChartPoint(a.X - b.X, a.Z - b.Z);
		}

		public static ChartPoint operator *(ChartPoint a, float b)
		{
			return new ChartPoint(a.X * b, a.Z * b);
		}

		public static float Dot(ChartPoint a, ChartPoint b)
		{
			return a.X * b.X + a.Z * b.Z;
		}

		public static ChartPoint Lerp(ChartPoint a, ChartPoint b, float t)
		{
			return a + (b - a) * t;
		}
	}
	public sealed class ChartStroke
	{
		public float Width;

		public readonly List<ChartPoint> Points = new List<ChartPoint>();

		public ChartStroke(float width)
		{
			Width = width;
		}
	}
	public sealed class ChartDocument
	{
		public const int MaxPoints = 200000;

		public const int MaxStrokes = 20000;

		public const int MaxStrokePoints = 4096;

		public readonly List<ChartStroke> Strokes = new List<ChartStroke>();

		public ChartPoint Center;

		public float Zoom = 0.1f;

		public int PointCount;

		public bool Erase(ChartPoint from, ChartPoint to, float radius)
		{
			List<ChartStroke> list = new List<ChartStroke>();
			bool flag = false;
			foreach (ChartStroke stroke in Strokes)
			{
				bool changed;
				List<ChartStroke> collection = ChartGeometry.Erase(stroke, from, to, radius, out changed);
				flag = flag || changed;
				list.AddRange(collection);
			}
			int num = 0;
			foreach (ChartStroke item in list)
			{
				num += item.Points.Count;
			}
			if (!flag || list.Count > 20000 || num > 200000)
			{
				return false;
			}
			Strokes.Clear();
			Strokes.AddRange(list);
			PointCount = num;
			return true;
		}
	}
	public static class ChartGeometry
	{
		public static ChartPoint ToWorld(ChartPoint screen, ChartPoint center, float worldPerPixel)
		{
			return center + screen * worldPerPixel;
		}

		public static ChartPoint ToScreen(ChartPoint world, ChartPoint center, float worldPerPixel)
		{
			return (world - center) * (1f / worldPerPixel);
		}

		public static float DistanceSquared(ChartPoint point, ChartPoint a, ChartPoint b)
		{
			ChartPoint b2 = b - a;
			float t = ((b2.LengthSquared < 1E-12f) ? 0f : Clamp(ChartPoint.Dot(point - a, b2) / b2.LengthSquared, 0f, 1f));
			return (point - ChartPoint.Lerp(a, b, t)).LengthSquared;
		}

		public static List<ChartStroke> Erase(ChartStroke stroke, ChartPoint from, ChartPoint to, float radius, out bool changed)
		{
			changed = false;
			float num = radius + stroke.Width * 0.5f;
			float num2 = num * num;
			List<ChartStroke> list = new List<ChartStroke>();
			if (stroke.Points.Count == 1)
			{
				changed = DistanceSquared(stroke.Points[0], from, to) <= num2;
				if (!changed)
				{
					list.Add(stroke);
				}
				return list;
			}
			ChartStroke chartStroke = null;
			for (int i = 1; i < stroke.Points.Count; i++)
			{
				ChartPoint chartPoint = stroke.Points[i - 1];
				ChartPoint chartPoint2 = stroke.Points[i];
				ChartPoint chartPoint3 = chartPoint2 - chartPoint;
				List<float> list2 = new List<float> { 0f, 1f };
				CircleCuts(chartPoint, chartPoint3, from, num, list2);
				CircleCuts(chartPoint, chartPoint3, to, num, list2);
				ChartPoint chartPoint4 = to - from;
				if (chartPoint4.LengthSquared > 1E-12f)
				{
					ChartPoint b = new ChartPoint(0f - chartPoint4.Z, chartPoint4.X) * (1f / (float)Math.Sqrt(chartPoint4.LengthSquared));
					float num3 = ChartPoint.Dot(chartPoint3, b);
					if (Math.Abs(num3) > 1E-09f)
					{
						float num4 = ChartPoint.Dot(chartPoint - from, b);
						AddCut((num - num4) / num3, list2);
						AddCut((0f - num - num4) / num3, list2);
					}
				}
				list2.Sort();
				for (int j = 1; j < list2.Count; j++)
				{
					float num5 = list2[j - 1];
					float num6 = list2[j];
					if (num6 - num5 < 1E-07f)
					{
						continue;
					}
					if (DistanceSquared(ChartPoint.Lerp(chartPoint, chartPoint2, (num5 + num6) * 0.5f), from, to) < num2)
					{
						changed = true;
						chartStroke = null;
						continue;
					}
					ChartPoint item = ChartPoint.Lerp(chartPoint, chartPoint2, num5);
					ChartPoint item2 = ChartPoint.Lerp(chartPoint, chartPoint2, num6);
					if (chartStroke == null)
					{
						chartStroke = new ChartStroke(stroke.Width);
						chartStroke.Points.Add(item);
						list.Add(chartStroke);
					}
					chartStroke.Points.Add(item2);
				}
			}
			if (!changed)
			{
				return new List<ChartStroke> { stroke };
			}
			return list;
		}

		private static void CircleCuts(ChartPoint a, ChartPoint d, ChartPoint center, float radius, List<float> cuts)
		{
			double num = (double)d.X * (double)d.X + (double)d.Z * (double)d.Z;
			if (!(num < 1E-12))
			{
				ChartPoint chartPoint = a - center;
				double num2 = 2.0 * ((double)chartPoint.X * (double)d.X + (double)chartPoint.Z * (double)d.Z);
				double num3 = (double)chartPoint.X * (double)chartPoint.X + (double)chartPoint.Z * (double)chartPoint.Z - (double)radius * (double)radius;
				double num4 = num2 * num2 - 4.0 * num * num3;
				if (!(num4 < 0.0))
				{
					double num5 = Math.Sqrt(num4);
					AddCut((float)((0.0 - num2 - num5) / (2.0 * num)), cuts);
					AddCut((float)((0.0 - num2 + num5) / (2.0 * num)), cuts);
				}
			}
		}

		private static void AddCut(float t, List<float> cuts)
		{
			if (t > 0f && t < 1f)
			{
				cuts.Add(t);
			}
		}

		public static bool ClipLine(ref ChartPoint a, ref ChartPoint b, float halfWidth, float halfHeight)
		{
			ChartPoint chartPoint = b - a;
			float lo = 0f;
			float hi = 1f;
			if (!Clip(0f - chartPoint.X, a.X + halfWidth, ref lo, ref hi) || !Clip(chartPoint.X, halfWidth - a.X, ref lo, ref hi) || !Clip(0f - chartPoint.Z, a.Z + halfHeight, ref lo, ref hi) || !Clip(chartPoint.Z, halfHeight - a.Z, ref lo, ref hi))
			{
				return false;
			}
			b = a + chartPoint * hi;
			a += chartPoint * lo;
			return true;
		}

		private static bool Clip(float p, float q, ref float lo, ref float hi)
		{
			if (Math.Abs(p) < 1E-12f)
			{
				return q >= 0f;
			}
			float num = q / p;
			if (p < 0f)
			{
				if (num > hi)
				{
					return false;
				}
				lo = Math.Max(lo, num);
			}
			else
			{
				if (num < lo)
				{
					return false;
				}
				hi = Math.Min(hi, num);
			}
			return true;
		}

		private static float Clamp(float value, float lo, float hi)
		{
			return Math.Max(lo, Math.Min(hi, value));
		}
	}
	internal readonly struct InkSegment
	{
		public readonly Vector2 A;

		public readonly Vector2 B;

		public readonly float Width;

		public readonly Color Color;

		public InkSegment(Vector2 a, Vector2 b, float width, Color color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			A = a;
			B = b;
			Width = width;
			Color = color;
		}
	}
	public sealed class ChartGraphic : MaskableGraphic
	{
		internal const int BatchSize = 6000;

		internal CanvasWindow Window;

		internal int Batch;

		protected override void OnPopulateMesh(VertexHelper mesh)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: 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_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: 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_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			mesh.Clear();
			if (Window == null)
			{
				return;
			}
			int num = Batch * 6000;
			int num2 = Mathf.Min(num + 6000, Window.Segments.Count);
			for (int i = num; i < num2; i++)
			{
				InkSegment inkSegment = Window.Segments[i];
				Vector2 val = inkSegment.B - inkSegment.A;
				? val2;
				if (!(((Vector2)(ref val)).sqrMagnitude > 1E-06f))
				{
					val2 = new Vector2(0f, inkSegment.Width * 0.5f);
				}
				else
				{
					Vector2 val3 = new Vector2(0f - val.y, val.x);
					val2 = ((Vector2)(ref val3)).normalized * (inkSegment.Width * 0.5f);
				}
				Vector2 val4 = (Vector2)val2;
				Vector2 val5 = (Vector2)((((Vector2)(ref val)).sqrMagnitude > 1E-06f) ? (((Vector2)(ref val)).normalized * (inkSegment.Width * 0.5f)) : new Vector2(inkSegment.Width * 0.5f, 0f));
				int currentVertCount = mesh.currentVertCount;
				Add(mesh, inkSegment.A - val5 - val4, inkSegment.Color);
				Add(mesh, inkSegment.A - val5 + val4, inkSegment.Color);
				Add(mesh, inkSegment.B + val5 + val4, inkSegment.Color);
				Add(mesh, inkSegment.B + val5 - val4, inkSegment.Color);
				mesh.AddTriangle(currentVertCount, currentVertCount + 1, currentVertCount + 2);
				mesh.AddTriangle(currentVertCount, currentVertCount + 2, currentVertCount + 3);
			}
		}

		private static void Add(VertexHelper mesh, Vector2 position, Color color)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			UIVertex simpleVert = UIVertex.simpleVert;
			simpleVert.position = Vector2.op_Implicit(position);
			simpleVert.color = Color32.op_Implicit(color);
			simpleVert.uv0 = Vector4.op_Implicit(Vector2.zero);
			mesh.AddVert(simpleVert);
		}
	}
	internal static class ChartStore
	{
		private const int Magic = 1212435761;

		private const int Version = 1;

		public static ChartDocument Load(string path)
		{
			ChartDocument chartDocument = new ChartDocument();
			if (!File.Exists(path))
			{
				return chartDocument;
			}
			using BinaryReader binaryReader = new BinaryReader(File.OpenRead(path));
			if (binaryReader.ReadInt32() != 1212435761 || binaryReader.ReadInt32() != 1)
			{
				throw new InvalidDataException("Unsupported chart file.");
			}
			chartDocument.Center = ReadPoint(binaryReader);
			chartDocument.Zoom = ReadFinite(binaryReader);
			if (chartDocument.Zoom <= 0f || chartDocument.Zoom > 10f)
			{
				throw new InvalidDataException("Invalid zoom.");
			}
			int num = ReadCount(binaryReader, 20000);
			for (int i = 0; i < num; i++)
			{
				float num2 = ReadFinite(binaryReader);
				if (num2 <= 0f || num2 > 100000f)
				{
					throw new InvalidDataException("Invalid stroke width.");
				}
				ChartStroke chartStroke = new ChartStroke(num2);
				int num3 = ReadCount(binaryReader, 200000);
				if (num3 == 0 || chartDocument.PointCount + num3 > 200000)
				{
					throw new InvalidDataException("Invalid point count.");
				}
				for (int j = 0; j < num3; j++)
				{
					chartStroke.Points.Add(ReadPoint(binaryReader));
				}
				chartDocument.PointCount += num3;
				chartDocument.Strokes.Add(chartStroke);
			}
			if (binaryReader.BaseStream.Position != binaryReader.BaseStream.Length)
			{
				throw new InvalidDataException("Unexpected chart data.");
			}
			return chartDocument;
		}

		public static void Save(string path, ChartDocument document)
		{
			Directory.CreateDirectory(Path.GetDirectoryName(path));
			string text = path + ".tmp";
			using (FileStream fileStream = new FileStream(text, FileMode.Create, FileAccess.Write, FileShare.None))
			{
				using (BinaryWriter binaryWriter = new BinaryWriter(fileStream, Encoding.UTF8, leaveOpen: true))
				{
					binaryWriter.Write(1212435761);
					binaryWriter.Write(1);
					WritePoint(binaryWriter, document.Center);
					binaryWriter.Write(document.Zoom);
					binaryWriter.Write(document.Strokes.Count);
					foreach (ChartStroke stroke in document.Strokes)
					{
						binaryWriter.Write(stroke.Width);
						binaryWriter.Write(stroke.Points.Count);
						foreach (ChartPoint point in stroke.Points)
						{
							WritePoint(binaryWriter, point);
						}
					}
				}
				fileStream.Flush(flushToDisk: true);
			}
			if (File.Exists(path))
			{
				File.Replace(text, path, path + ".bak");
			}
			else
			{
				File.Move(text, path);
			}
		}

		private static int ReadCount(BinaryReader reader, int maximum)
		{
			int num = reader.ReadInt32();
			if (num < 0 || num > maximum)
			{
				throw new InvalidDataException("Chart exceeds size limits.");
			}
			return num;
		}

		private static float ReadFinite(BinaryReader reader)
		{
			float num = reader.ReadSingle();
			if (float.IsNaN(num) || float.IsInfinity(num) || Math.Abs(num) > 10000000f)
			{
				throw new InvalidDataException("Invalid chart coordinate.");
			}
			return num;
		}

		private static ChartPoint ReadPoint(BinaryReader reader)
		{
			return new ChartPoint(ReadFinite(reader), ReadFinite(reader));
		}

		private static void WritePoint(BinaryWriter writer, ChartPoint point)
		{
			writer.Write(point.X);
			writer.Write(point.Z);
		}
	}
	public sealed class InkHistory
	{
		private sealed class Edit
		{
			public readonly List<SavedStroke> Added = new List<SavedStroke>();

			public readonly List<SavedStroke> Removed = new List<SavedStroke>();

			public int AddedPoints => Added.Sum((SavedStroke stroke) => stroke.Points.Length);

			public int RemovedPoints => Removed.Sum((SavedStroke stroke) => stroke.Points.Length);

			public int PointCount => AddedPoints + RemovedPoints;
		}

		private sealed class SavedStroke
		{
			public readonly ChartStroke Stroke;

			public readonly ChartPoint[] Points;

			public readonly int Index;

			private readonly float width;

			public SavedStroke(ChartStroke stroke, int index)
			{
				Stroke = stroke;
				Points = stroke.Points.ToArray();
				Index = index;
				width = stroke.Width;
			}

			public bool Matches()
			{
				if (Stroke.Width != width || Stroke.Points.Count != Points.Length)
				{
					return false;
				}
				for (int i = 0; i < Points.Length; i++)
				{
					if (Stroke.Points[i].X != Points[i].X || Stroke.Points[i].Z != Points[i].Z)
					{
						return false;
					}
				}
				return true;
			}
		}

		private const int MaxActions = 50;

		private const int MaxRetainedPoints = 1000000;

		private readonly List<Edit> edits = new List<Edit>();

		private List<ChartStroke> before;

		private int retainedPoints;

		public int Count => edits.Count;

		public void Begin(ChartDocument document)
		{
			if (before == null)
			{
				before = new List<ChartStroke>(document.Strokes);
			}
		}

		public void Commit(ChartDocument document)
		{
			if (before == null)
			{
				return;
			}
			HashSet<ChartStroke> hashSet = new HashSet<ChartStroke>(before);
			HashSet<ChartStroke> hashSet2 = new HashSet<ChartStroke>(document.Strokes);
			Edit edit = new Edit();
			for (int i = 0; i < before.Count; i++)
			{
				if (!hashSet2.Contains(before[i]))
				{
					edit.Removed.Add(new SavedStroke(before[i], i));
				}
			}
			for (int j = 0; j < document.Strokes.Count; j++)
			{
				if (!hashSet.Contains(document.Strokes[j]))
				{
					edit.Added.Add(new SavedStroke(document.Strokes[j], j));
				}
			}
			before = null;
			if (edit.Added.Count != 0 || edit.Removed.Count != 0)
			{
				edits.Add(edit);
				retainedPoints += edit.PointCount;
				while (edits.Count > 50 || retainedPoints > 1000000)
				{
					retainedPoints -= edits[0].PointCount;
					edits.RemoveAt(0);
				}
			}
		}

		public bool Undo(ChartDocument document, out bool conflict)
		{
			conflict = false;
			if (before != null || edits.Count == 0)
			{
				return false;
			}
			Edit edit = edits[edits.Count - 1];
			HashSet<ChartStroke> hashSet = new HashSet<ChartStroke>(document.Strokes);
			foreach (SavedStroke item in edit.Added)
			{
				if (!hashSet.Contains(item.Stroke) || !item.Matches())
				{
					conflict = true;
					return false;
				}
			}
			foreach (SavedStroke item2 in edit.Removed)
			{
				if (hashSet.Contains(item2.Stroke) || !item2.Matches())
				{
					conflict = true;
					return false;
				}
			}
			int num = document.PointCount - edit.AddedPoints + edit.RemovedPoints;
			int num2 = document.Strokes.Count - edit.Added.Count + edit.Removed.Count;
			if (num > 200000 || num2 > 20000)
			{
				conflict = true;
				return false;
			}
			foreach (SavedStroke item3 in edit.Added)
			{
				document.Strokes.Remove(item3.Stroke);
			}
			foreach (SavedStroke item4 in edit.Removed)
			{
				document.Strokes.Insert(Math.Min(item4.Index, document.Strokes.Count), item4.Stroke);
			}
			document.PointCount = num;
			retainedPoints -= edit.PointCount;
			edits.RemoveAt(edits.Count - 1);
			return true;
		}
	}
	public static class MetricScale
	{
		public static float Length(float worldPerPixel, float targetPixels)
		{
			double num = worldPerPixel * targetPixels;
			if (num <= 0.0 || double.IsInfinity(num) || double.IsNaN(num))
			{
				return 1f;
			}
			double num2 = Math.Pow(10.0, Math.Floor(Math.Log10(num)));
			double num3 = num / num2;
			return (float)((double)((num3 >= 5.0) ? 5 : ((!(num3 >= 2.0)) ? 1 : 2)) * num2);
		}

		public static string Label(float meters)
		{
			if (!(meters >= 1000f))
			{
				return meters.ToString("0.##", CultureInfo.InvariantCulture) + " m";
			}
			return (meters / 1000f).ToString("0.##", CultureInfo.InvariantCulture) + " km";
		}
	}
	public sealed class MapJournal : IDisposable
	{
		private sealed class Record
		{
			public long Revision;

			public byte[] Bytes;
		}

		private const int Magic = 1212435762;

		private readonly string path;

		private readonly ConcurrentQueue<Record> queue = new ConcurrentQueue<Record>();

		private readonly object gate = new object();

		private Task worker = Task.CompletedTask;

		private long durable;

		private Exception failure;

		private long bytes;

		public readonly Guid Generation;

		public readonly SharedMap Loaded = new SharedMap();

		public bool Corrupt { get; private set; }

		public long Durable
		{
			get
			{
				lock (gate)
				{
					return durable;
				}
			}
		}

		public Exception Failure
		{
			get
			{
				lock (gate)
				{
					return failure;
				}
			}
		}

		public bool HasRoom(int count)
		{
			return bytes + count + 36 < 65011712;
		}

		public MapJournal(string path, long world)
		{
			this.path = path;
			Directory.CreateDirectory(Path.GetDirectoryName(path));
			if (File.Exists(path))
			{
				if (new FileInfo(path).Length > 67108864)
				{
					throw new InvalidDataException("Shared map journal too large");
				}
				using (BinaryReader binaryReader = new BinaryReader(File.OpenRead(path)))
				{
					if (binaryReader.ReadInt32() != 1212435762 || binaryReader.ReadInt32() != 1 || binaryReader.ReadInt64() != world)
					{
						throw new InvalidDataException("Invalid shared map journal header");
					}
					Generation = MapWire.ReadGuid(binaryReader);
					while (binaryReader.BaseStream.Position < binaryReader.BaseStream.Length)
					{
						try
						{
							int num = MapWire.Count(binaryReader, 32768);
							if (num == 0)
							{
								throw new InvalidDataException();
							}
							byte[] array = binaryReader.ReadBytes(32);
							byte[] array2 = binaryReader.ReadBytes(num);
							if (array.Length != 32 || array2.Length != num)
							{
								throw new EndOfStreamException();
							}
							using (SHA256 sHA = SHA256.Create())
							{
								if (!sHA.ComputeHash(array2).SequenceEqual(array))
								{
									throw new InvalidDataException("Journal checksum mismatch");
								}
							}
							Loaded.Apply(MapWire.DecodeEdit(array2));
						}
						catch (Exception ex) when (ex is IOException || ex is InvalidOperationException)
						{
							Corrupt = true;
							break;
						}
					}
				}
				bytes = new FileInfo(path).Length;
				if (!Corrupt)
				{
					File.Copy(path, path + ".bak", overwrite: true);
				}
			}
			else
			{
				Generation = Guid.NewGuid();
				using (FileStream fileStream = new FileStream(path, FileMode.CreateNew, FileAccess.Write, FileShare.Read))
				{
					using BinaryWriter binaryWriter = new BinaryWriter(fileStream);
					binaryWriter.Write(1212435762);
					binaryWriter.Write(1);
					binaryWriter.Write(world);
					binaryWriter.Write(Generation.ToByteArray());
					binaryWriter.Flush();
					fileStream.Flush(flushToDisk: true);
				}
				bytes = new FileInfo(path).Length;
			}
			durable = Loaded.Revision;
		}

		public void Append(MapEdit edit)
		{
			byte[] array = MapWire.EncodeEdit(edit);
			lock (gate)
			{
				if (Corrupt || failure != null)
				{
					throw new IOException("Shared map storage is read-only", failure);
				}
				if (!HasRoom(array.Length))
				{
					throw new IOException("Shared map storage limit reached");
				}
				bytes += array.Length + 36;
				queue.Enqueue(new Record
				{
					Revision = edit.Revision,
					Bytes = array
				});
				worker = worker.ContinueWith(delegate
				{
					Drain();
				}, TaskScheduler.Default);
			}
		}

		private void Drain()
		{
			lock (gate)
			{
				if (failure != null)
				{
					return;
				}
			}
			if (queue.IsEmpty)
			{
				return;
			}
			try
			{
				long val = 0L;
				using (FileStream fileStream = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.Read))
				{
					using BinaryWriter binaryWriter = new BinaryWriter(fileStream);
					using SHA256 sHA = SHA256.Create();
					Record result;
					while (queue.TryDequeue(out result))
					{
						binaryWriter.Write(result.Bytes.Length);
						binaryWriter.Write(sHA.ComputeHash(result.Bytes));
						binaryWriter.Write(result.Bytes);
						val = result.Revision;
					}
					binaryWriter.Flush();
					fileStream.Flush(flushToDisk: true);
				}
				lock (gate)
				{
					durable = Math.Max(durable, val);
				}
			}
			catch (Exception ex)
			{
				lock (gate)
				{
					failure = ex;
				}
			}
		}

		public void Flush()
		{
			Task task;
			lock (gate)
			{
				task = worker;
			}
			task.GetAwaiter().GetResult();
		}

		public void Dispose()
		{
			Flush();
		}
	}
	[BepInPlugin("com.nopetrides.valheim.hand-drawn-map", "Hand Drawn Map", "0.4.1")]
	[DefaultExecutionOrder(-10000)]
	public sealed class MapPlugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(Minimap), "IsOpen")]
		private static class MapInput
		{
			private static void Postfix(ref bool __result)
			{
				if (BlocksInput)
				{
					__result = true;
				}
			}
		}

		[HarmonyPatch(typeof(Minimap), "Update")]
		private static class MapUpdate
		{
			private static bool Prefix()
			{
				Instance?.Poll();
				if (!IsOpen)
				{
					return !ConsumedInput;
				}
				return false;
			}
		}

		[HarmonyPatch(typeof(Menu), "Update")]
		private static class MenuUpdate
		{
			private static bool Prefix()
			{
				if (!IsOpen)
				{
					return !ConsumedInput;
				}
				return false;
			}
		}

		[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
		private static class PeerConnected
		{
			private static void Prefix(ZNetPeer __0)
			{
				Instance?.Shared?.Register(__0);
			}
		}

		[HarmonyPatch(typeof(ZNet), "Shutdown")]
		private static class WorldShutdown
		{
			private static void Prefix()
			{
				Instance?.Window?.FinishSharedGesture();
				Instance?.Shared?.Flush();
			}
		}

		public const string Guid = "com.nopetrides.valheim.hand-drawn-map";

		public const string Version = "0.4.1";

		internal static MapPlugin Instance;

		internal ConfigEntry<KeyboardShortcut> MapKey;

		internal ConfigEntry<float> SurveyLifetime;

		internal ConfigEntry<float> PencilPixels;

		internal ConfigEntry<float> EraserPixels;

		internal ConfigEntry<float> AnnotationLifetime;

		internal ConfigEntry<float> BrushOpacity;

		internal ConfigEntry<bool> ShowMyLocation;

		internal ConfigEntry<bool> PersonalMapOnly;

		internal readonly List<SurveyGuide> Guides = new List<SurveyGuide>();

		internal ChartDocument Document;

		internal CanvasWindow Window;

		internal bool Dirty;

		internal MapSession Shared;

		private bool headless;

		private Harmony harmony;

		private string savePath;

		private bool saveBlocked;

		private float lastSave;

		private int lastPoll = -1;

		private int consumedFrame = -10;

		private int blockThroughFrame = -10;

		private MapMode previousMode;

		internal bool Headless => headless;

		internal static bool IsOpen
		{
			get
			{
				if ((Object)(object)Instance != (Object)null && Instance.Window != null)
				{
					return Instance.Window.IsOpen;
				}
				return false;
			}
		}

		internal static bool BlocksInput
		{
			get
			{
				if (!IsOpen)
				{
					if ((Object)(object)Instance != (Object)null)
					{
						return Time.frameCount <= Instance.blockThroughFrame;
					}
					return false;
				}
				return true;
			}
		}

		internal static bool ConsumedInput
		{
			get
			{
				if ((Object)(object)Instance != (Object)null)
				{
					return Instance.consumedFrame == Time.frameCount;
				}
				return false;
			}
		}

		private void Awake()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Expected O, but got Unknown
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Expected O, but got Unknown
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Expected O, but got Unknown
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Expected O, but got Unknown
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Expected O, but got Unknown
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Expected O, but got Unknown
			headless = (int)SystemInfo.graphicsDeviceType == 4;
			Instance = this;
			MapKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "OpenMap", new KeyboardShortcut((KeyCode)109, Array.Empty<KeyCode>()), "[Local] Open or close the drawing canvas.");
			SurveyLifetime = ((BaseUnityPlugin)this).Config.Bind<float>("Drawing", "SurveyLifetimeSeconds", 120f, new ConfigDescription("[Local] How long your personal survey guides last.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 1800f), Array.Empty<object>()));
			PencilPixels = ((BaseUnityPlugin)this).Config.Bind<float>("Drawing", "PencilWidthPixels", 2.5f, new ConfigDescription("[Local] Width of new pencil and annotation strokes. Everyone sees the resulting ink at the same size.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 16f), Array.Empty<object>()));
			EraserPixels = ((BaseUnityPlugin)this).Config.Bind<float>("Drawing", "EraserRadiusPixels", 14f, new ConfigDescription("[Local] Size of your eraser. Shared-map erasing is visible to everyone.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(3f, 100f), Array.Empty<object>()));
			AnnotationLifetime = ((BaseUnityPlugin)this).Config.Bind<float>("Drawing", "AnnotationLifetimeSeconds", 120f, new ConfigDescription("[Server-controlled] How long annotations last. Shared maps use the host or server's value for everyone; your value applies only when hosting or using a personal map.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 1800f), Array.Empty<object>()));
			BrushOpacity = ((BaseUnityPlugin)this).Config.Bind<float>("Drawing", "BrushIndicatorOpacity", 0.2f, new ConfigDescription("[Local] Opacity of your brush indicator.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			ShowMyLocation = ((BaseUnityPlugin)this).Config.Bind<bool>("Map", "ShowMyLocation", false, "[Local] Show your current position and camera heading on the drawing canvas.");
			PersonalMapOnly = ((BaseUnityPlugin)this).Config.Bind<bool>("Map", "PersonalMapOnly", false, "[Local] Use a personal map immediately, without connecting to the host's shared map.");
			Shared = new MapSession(this);
			harmony = new Harmony("com.nopetrides.valheim.hand-drawn-map");
			harmony.PatchAll(typeof(MapPlugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Hand Drawn Map 0.4.1 loaded.");
		}

		private void Update()
		{
			Shared.Tick();
			if (!headless)
			{
				Poll();
			}
		}

		internal void Poll()
		{
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			if (lastPoll == Time.frameCount)
			{
				return;
			}
			lastPoll = Time.frameCount;
			Shared.Tick();
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)ZNet.instance == (Object)null || (Object)(object)Minimap.instance == (Object)null)
			{
				ReleaseWorld();
				return;
			}
			long worldUID = ZNet.instance.GetWorldUID();
			long playerID = localPlayer.GetPlayerID();
			if (worldUID == 0L || playerID == 0L)
			{
				return;
			}
			string text = Path.Combine(Paths.ConfigPath, "com.nopetrides.valheim.hand-drawn-map", worldUID + "_" + playerID + ".view.chart");
			if (text != savePath)
			{
				LoadWorld(text);
			}
			if (IsOpen && (!Application.isFocused || ((Character)localPlayer).IsDead() || ((Character)localPlayer).IsTeleporting() || ((Character)localPlayer).InCutscene() || OtherUiOpen()))
			{
				CloseMap();
			}
			if (ShortcutDown(MapKey.Value) && !OtherUiOpen())
			{
				consumedFrame = Time.frameCount;
				if (IsOpen)
				{
					CloseMap();
				}
				else if (CanUseWorldInput(localPlayer))
				{
					OpenMap();
				}
			}
			bool flag = SurveyTool.IsEquipped(localPlayer) && Input.GetMouseButtonDown(1);
			if (!IsOpen && flag && CanUseWorldInput(localPlayer))
			{
				AddSurvey(localPlayer);
			}
			if (IsOpen)
			{
				Window.Tick();
			}
			if (Guides.RemoveAll((SurveyGuide guide) => guide.Expires <= Time.unscaledTime) > 0)
			{
				Window?.Invalidate();
			}
			if (Dirty && Time.unscaledTime - lastSave > 5f && (Window == null || !Window.InGesture))
			{
				Save();
			}
		}

		private static bool ShortcutDown(KeyboardShortcut shortcut)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0 || !Input.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey))
			{
				return false;
			}
			foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers)
			{
				if (!Input.GetKey(modifier))
				{
					return false;
				}
			}
			return true;
		}

		private static bool CanUseWorldInput(Player player)
		{
			if (Application.isFocused && !OtherUiOpen() && !Minimap.IsOpen() && !((Character)player).IsDead() && !((Character)player).IsTeleporting() && !((Character)player).InCutscene() && !((Character)player).InAttack())
			{
				return !((Character)player).InDodge();
			}
			return false;
		}

		private static bool OtherUiOpen()
		{
			if (!InventoryGui.IsVisible() && !Menu.IsVisible() && !Console.IsVisible() && !TextInput.IsVisible() && !StoreGui.IsVisible() && (!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()) && !ZInput.VirtualKeyboardOpen)
			{
				return UnifiedPopup.IsVisible();
			}
			return true;
		}

		private void LoadWorld(string path)
		{
			ReleaseWorld();
			savePath = path;
			saveBlocked = false;
			try
			{
				bool num = File.Exists(path);
				string text = path.Replace(".view.chart", ".chart");
				ChartDocument chartDocument = ChartStore.Load(num ? path : text);
				Document = new ChartDocument
				{
					Center = chartDocument.Center,
					Zoom = chartDocument.Zoom
				};
				if (!num)
				{
					Document.Zoom = Minimap.instance.LargeZoom;
				}
			}
			catch (Exception ex)
			{
				saveBlocked = true;
				Document = new ChartDocument();
				((BaseUnityPlugin)this).Logger.LogError((object)("Chart could not be loaded; saving disabled to preserve the file: " + ex.Message));
			}
			Window = new CanvasWindow(this, readOnly: false);
			Shared.Bind(Document, delegate
			{
				Window?.Invalidate();
			});
			lastSave = Time.unscaledTime;
		}

		private void OpenMap()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			previousMode = Minimap.instance.m_mode;
			Minimap.instance.SetMapMode((MapMode)0);
			Window.SetOpen(open: true);
			ZCursor.LockState = (CursorLockMode)0;
			ZCursor.Show();
		}

		internal void CloseMap()
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			if (IsOpen)
			{
				Window.SetOpen(open: false);
				blockThroughFrame = Time.frameCount + 2;
				consumedFrame = Time.frameCount;
				if ((Object)(object)Minimap.instance != (Object)null)
				{
					Minimap.instance.SetMapMode((MapMode)((!Game.m_noMap) ? ((int)previousMode) : 0));
				}
				Save();
			}
		}

		private void AddSurvey(Player player)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: 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)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)GameCamera.instance == (Object)null)
			{
				return;
			}
			Vector3 forward = ((Component)GameCamera.instance).transform.forward;
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(forward.x, forward.z);
			if (!(((Vector2)(ref val)).sqrMagnitude < 0.001f))
			{
				((Vector2)(ref val)).Normalize();
				Vector3 position = ((Component)player).transform.position;
				if (Guides.Count >= 32)
				{
					Guides.RemoveAt(0);
				}
				Guides.Add(new SurveyGuide
				{
					Origin = new ChartPoint(position.x, position.z),
					Direction = new ChartPoint(val.x, val.y),
					Created = Time.unscaledTime,
					Expires = Time.unscaledTime + SurveyLifetime.Value
				});
				Window.Invalidate();
				if ((Object)(object)MessageHud.instance != (Object)null)
				{
					MessageHud.instance.ShowMessage((MessageType)1, "You sketch a guide line across your map.", 0, (Sprite)null, false, true);
				}
			}
		}

		internal void LogToolError(Exception exception)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("Survey Compass registration failed: " + exception));
		}

		internal void LogToolWarning(string message)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)message);
		}

		internal void LogMapInfo(string message)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)message);
		}

		internal void Save()
		{
			if (!Dirty || savePath == null || saveBlocked)
			{
				return;
			}
			lastSave = Time.unscaledTime;
			try
			{
				ChartStore.Save(savePath, new ChartDocument
				{
					Center = Document.Center,
					Zoom = Document.Zoom
				});
				Dirty = false;
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Chart save failed: " + ex.Message));
			}
		}

		private void ReleaseWorld()
		{
			CloseMap();
			Save();
			Shared?.Unbind();
			Window?.Dispose();
			Window = null;
			Document = null;
			savePath = null;
			Guides.Clear();
			Dirty = false;
		}

		private void OnApplicationFocus(bool focused)
		{
			if (!focused)
			{
				CloseMap();
			}
		}

		private void OnApplicationQuit()
		{
			Window?.FinishSharedGesture();
			Save();
			Shared?.Flush();
		}

		private void OnDestroy()
		{
			ReleaseWorld();
			Shared?.Dispose();
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}
	}
	internal sealed class SurveyGuide
	{
		public ChartPoint Origin;

		public ChartPoint Direction;

		public float Created;

		public float Expires;
	}
	internal sealed class MapSession : IDisposable
	{
		private enum Message : byte
		{
			Hello,
			Welcome,
			Edit,
			Commit,
			Reject,
			Start,
			Part,
			Finish,
			Resync,
			Ping,
			Pong
		}

		private sealed class Peer
		{
			public ZNetPeer Network;

			public Guid Session;

			public long Actor;

			public bool Welcomed;

			public readonly MapEditorGuard Guard = new MapEditorGuard();

			public float LastSnapshot = -100f;

			public readonly Dictionary<Guid, string> Rejected = new Dictionary<Guid, string>();
		}

		private sealed class Transfer
		{
			public Peer Peer;

			public Guid Id = Guid.NewGuid();

			public byte[] Bytes;

			public byte[] Hash;

			public int Offset;

			public long Revision;

			public bool Started;
		}

		private sealed class Download
		{
			public Guid Id;

			public byte[] Bytes;

			public byte[] Hash;

			public int Offset;

			public long Revision;
		}

		private const string RpcName = "NPR_SharedMap_V1";

		private readonly MapPlugin plugin;

		private readonly Dictionary<ZRpc, Peer> peers = new Dictionary<ZRpc, Peer>();

		private readonly List<MapEdit> outgoing = new List<MapEdit>();

		private readonly Dictionary<Guid, MapEdit> inflight = new Dictionary<Guid, MapEdit>();

		private readonly List<MapEdit> received = new List<MapEdit>();

		private readonly List<Transfer> transfers = new List<Transfer>();

		private readonly List<MapEdit> waitingCommits = new List<MapEdit>();

		private MapReplica replica = new MapReplica();

		private SharedMap owner;

		private SharedMap published;

		private MapJournal journal;

		private ChartDocument view;

		private Action changed;

		private long ownerWorld;

		private long clientWorld;

		private long sequence;

		private MapEditorGuard localGuard = new MapEditorGuard();

		private Guid clientSession;

		private bool ownerFailed;

		private string ownerError;

		private float helloAt;

		private float nextSend;

		private float nextPing;

		private float nextFade;

		private float syncStarted;

		private int lastTick = -1;

		private float clientStarted;

		private double clockOffset;

		private Download download;

		private bool syncing;

		private bool personal;

		private bool personalOnly;

		private long character;

		internal readonly List<TemporaryInk> Annotations = new List<TemporaryInk>();

		internal bool CanEdit
		{
			get
			{
				if (replica.Ready && !syncing && !ownerFailed && replica.Pending.Count < 256)
				{
					return inflight.Count < 256;
				}
				return false;
			}
		}

		internal string Status
		{
			get
			{
				object obj = ownerError;
				if (obj == null)
				{
					obj = (ownerFailed ? "Map is read-only" : null);
					if (obj == null)
					{
						obj = replica.Error;
						if (obj == null)
						{
							if (!syncing)
							{
								if (replica.Ready)
								{
									if (replica.Pending.Count <= 0)
									{
										if (!personal)
										{
											return "Shared map";
										}
										return "Personal map";
									}
									return "Pending edits: " + replica.Pending.Count;
								}
								return "Connecting to map owner...";
							}
							obj = "Downloading shared map...";
						}
					}
				}
				return (string)obj;
			}
		}

		internal double Now => UtcNow + clockOffset;

		private static double UtcNow => (double)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() / 1000.0;

		internal MapSession(MapPlugin plugin)
		{
			this.plugin = plugin;
		}

		internal void Register(ZNetPeer network)
		{
			if (!peers.ContainsKey(network.m_rpc))
			{
				Peer peer = new Peer
				{
					Network = network
				};
				peers.Add(network.m_rpc, peer);
				network.m_rpc.Register<ZPackage>("NPR_SharedMap_V1", (Action<ZRpc, ZPackage>)delegate(ZRpc rpc, ZPackage package)
				{
					Receive(peer, package);
				});
			}
		}

		internal void Bind(ChartDocument document, Action onChanged)
		{
			view = document;
			changed = onChanged;
			Refresh();
		}

		internal void Unbind()
		{
			view = null;
			changed = null;
			Annotations.Clear();
		}

		internal void Tick()
		{
			if (lastTick == Time.frameCount)
			{
				return;
			}
			lastTick = Time.frameCount;
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null)
			{
				StopOwner();
				ResetClient();
				peers.Clear();
			}
			else if (!instance.IsServer() && instance.GetServerPeer() == null)
			{
				if (replica.Ready)
				{
					SaveRecovery();
					replica.Ready = false;
					Refresh();
				}
			}
			else
			{
				if (instance.GetWorld() == null)
				{
					return;
				}
				long world = instance.GetWorldUID();
				if (world == 0L)
				{
					return;
				}
				long num = (((Object)(object)Player.m_localPlayer == (Object)null) ? 0 : Player.m_localPlayer.GetPlayerID());
				if (clientWorld != 0L && (clientWorld != world || character != num || personalOnly != plugin.PersonalMapOnly.Value))
				{
					plugin.Window?.FinishSharedGesture();
					Flush();
					StopOwner();
					ResetClient();
				}
				if (owner != null && ownerWorld != world)
				{
					StopOwner();
				}
				bool flag = !instance.IsDedicated() && !plugin.Headless && num != 0;
				if (plugin.PersonalMapOnly.Value)
				{
					if (flag && !personal)
					{
						StartPersonal(world);
					}
					if (!flag)
					{
						StopOwner();
						return;
					}
				}
				if (!personal && !plugin.PersonalMapOnly.Value && instance.IsServer() && owner == null && (instance.IsDedicated() || plugin.Headless || flag))
				{
					StartOwner(world);
				}
				if (flag && (clientWorld != world || (owner != null && !replica.Ready)))
				{
					StartClient(world);
				}
				Peer[] array = peers.Values.ToArray();
				foreach (Peer peer in array)
				{
					if (!instance.GetPeers().Contains(peer.Network))
					{
						peers.Remove(peer.Network.m_rpc);
						transfers.RemoveAll((Transfer t) => t.Peer == peer);
					}
				}
				if (journal != null)
				{
					if (journal.Failure != null && !ownerFailed)
					{
						ownerFailed = true;
						ownerError = "Shared map storage failed; editing paused";
						plugin.LogToolError(journal.Failure);
						MapEdit[] array2 = waitingCommits.ToArray();
						foreach (MapEdit mapEdit in array2)
						{
							RejectAll(mapEdit.Id, ownerError);
						}
						waitingCommits.Clear();
					}
					if (!ownerFailed)
					{
						while (waitingCommits.Count > 0 && waitingCommits[0].Revision <= journal.Durable)
						{
							MapEdit edit = waitingCommits[0];
							waitingCommits.RemoveAt(0);
							published.Apply(edit);
							if (clientWorld == ownerWorld && replica.Ready)
							{
								Accept(edit);
							}
							foreach (Peer item in peers.Values.Where((Peer p) => !personal && p.Welcomed))
							{
								Send(item, Message.Commit, delegate(BinaryWriter writer)
								{
									MapWire.WriteEdit(writer, edit);
								});
							}
						}
					}
				}
				if (owner != null && !personal)
				{
					PumpTransfers();
				}
				if (!personal && !instance.IsServer() && clientWorld == world)
				{
					ZNetPeer serverPeer = instance.GetServerPeer();
					if (serverPeer != null && serverPeer.m_uid != 0L && !replica.Ready && !syncing && Time.unscaledTime - helloAt > 2f)
					{
						helloAt = Time.unscaledTime;
						if (peers.TryGetValue(serverPeer.m_rpc, out var value))
						{
							Send(value, Message.Hello, delegate(BinaryWriter writer)
							{
								writer.Write("0.4.1");
								writer.Write(world);
							});
						}
					}
					if (serverPeer == null && replica.Ready)
					{
						SaveRecovery();
						replica.Ready = false;
					}
					if (serverPeer != null && clientSession == Guid.Empty && Time.unscaledTime - clientStarted > 10f)
					{
						StartPersonal(world);
						plugin.LogMapInfo("No shared map response; using a personal map for this session.");
					}
					if (!personal && Time.unscaledTime >= nextSend && serverPeer != null && clientSession != Guid.Empty && !syncing)
					{
						nextSend = Time.unscaledTime + 0.05f;
						if (peers.TryGetValue(serverPeer.m_rpc, out var value2))
						{
							MapEdit[] array2 = outgoing.ToArray();
							foreach (MapEdit edit2 in array2)
							{
								Send(value2, Message.Edit, delegate(BinaryWriter writer)
								{
									writer.Write(clientSession.ToByteArray());
									MapWire.WriteEdit(writer, edit2);
								});
								outgoing.Remove(edit2);
							}
						}
					}
					if (!personal && replica.Ready && Time.unscaledTime > nextPing && serverPeer != null && peers.TryGetValue(serverPeer.m_rpc, out var value3))
					{
						nextPing = Time.unscaledTime + 3f;
						Send(value3, Message.Ping, delegate(BinaryWriter writer)
						{
							writer.Write(replica.Confirmed.Revision);
						});
					}
				}
				if (syncing && Time.unscaledTime - syncStarted > 30f)
				{
					syncing = false;
					download = null;
					replica.Error = "Map sync timed out; press Resync";
					replica.Ready = false;
				}
				if (replica.Ready && Annotations.Count > 0 && Time.unscaledTime > nextFade)
				{
					nextFade = Time.unscaledTime + 0.05f;
					Refresh();
				}
			}
		}

		private void StartOwner(long world)
		{
			ownerWorld = world;
			string path = (personal ? Path.Combine(Paths.ConfigPath, "com.nopetrides.valheim.hand-drawn-map", "personal", world + "_" + Player.m_localPlayer.GetPlayerID() + ".journal") : Path.Combine(Paths.ConfigPath, "com.nopetrides.valheim.hand-drawn-map", "shared", world + ".journal"));
			try
			{
				bool num = !File.Exists(path);
				journal = new MapJournal(path, world);
				owner = journal.Loaded;
				ownerFailed = journal.Corrupt;
				if (ownerFailed)
				{
					ownerError = "Map save is damaged; recovered drawings are read-only";
				}
				if (num && !ZNet.instance.IsDedicated() && !plugin.Headless)
				{
					ImportPersonalMap(world);
				}
				published = owner.Fork();
			}
			catch (Exception exception)
			{
				owner = new SharedMap();
				ownerFailed = true;
				published = new SharedMap();
				ownerError = "Map could not be loaded; editing paused";
				plugin.LogToolError(exception);
			}
		}

		private void ImportPersonalMap(long world)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			string path = Path.Combine(Paths.ConfigPath, "com.nopetrides.valheim.hand-drawn-map", world + "_" + localPlayer.GetPlayerID() + ".chart");
			if (!File.Exists(path))
			{
				return;
			}
			ChartDocument chartDocument;
			try
			{
				chartDocument = ChartStore.Load(path);
			}
			catch (Exception exception)
			{
				plugin.LogToolError(exception);
				return;
			}
			foreach (ChartStroke stroke in chartDocument.Strokes)
			{
				Guid gesture = Guid.NewGuid();
				for (int i = 0; i < stroke.Points.Count; i += 127)
				{
					MapEdit request = new MapEdit
					{
						Kind = MapEditKind.Draw,
						Gesture = gesture,
						Width = stroke.Width,
						Points = stroke.Points.Skip(i).Take(128).ToArray()
					};
					if (!owner.Prepare(request, 0L, UtcNow, 120f, out var edit, out var error))
					{
						throw new InvalidDataException(error);
					}
					owner.Apply(edit);
					journal.Append(owner.ForDisk(edit));
				}
				MapEdit request2 = new MapEdit
				{
					Kind = MapEditKind.End,
					Gesture = gesture
				};
				if (owner.Prepare(request2, 0L, UtcNow, 120f, out var edit2, out var _))
				{
					owner.Apply(edit2);
					journal.Append(edit2);
				}
			}
			journal.Flush();
			plugin.LogMapInfo("Imported existing personal ink into the new " + (personal ? "personal" : "shared") + " map; original file retained.");
		}

		private void StartPersonal(long world)
		{
			plugin.Window?.FinishSharedGesture();
			Flush();
			StopOwner();
			ResetClient();
			personal = true;
			StartOwner(world);
			StartClient(world);
		}

		private void StartClient(long world)
		{
			bool flag = personal;
			ResetClient();
			clientWorld = world;
			personal = flag;
			personalOnly = plugin.PersonalMapOnly.Value;
			character = (((Object)(object)Player.m_localPlayer == (Object)null) ? 0 : Player.m_localPlayer.GetPlayerID());
			localGuard = new MapEditorGuard();
			clientStarted = Time.unscaledTime;
			if ((personal || ZNet.instance.IsServer()) && owner != null)
			{
				clientSession = Guid.NewGuid();
				replica.Actor = NewActor();
				replica.AnnotationLifetime = plugin.AnnotationLifetime.Value;
				replica.Snapshot(owner.Fork(), UtcNow);
				Refresh();
			}
			else
			{
				helloAt = -100f;
			}
		}

		internal bool Draw(Guid gesture, ChartPoint[] points, float width, bool temporary)
		{
			return Submit(new MapEdit
			{
				Gesture = gesture,
				Kind = ((!temporary) ? MapEditKind.Draw : MapEditKind.Annotate),
				Points = points,
				Width = width
			});
		}

		internal bool Erase(Guid gesture, ChartPoint from, ChartPoint to, float radius)
		{
			if (!CanEdit)
			{
				return false;
			}
			Guid[] array = replica.Predicted.Targets(from, to, radius, Now);
			bool flag = false;
			for (int i = 0; i < array.Length; i += 1024)
			{
				flag |= Submit(new MapEdit
				{
					Gesture = gesture,
					Kind = MapEditKind.Erase,
					Points = new ChartPoint[2] { from, to },
					Width = radius,
					Targets = array.Skip(i).Take(1024).ToArray()
				});
			}
			return flag;
		}

		internal void End(Guid gesture)
		{
			Submit(new MapEdit
			{
				Kind = MapEditKind.End,
				Gesture = gesture
			});
		}

		internal bool Undo()
		{
			return Submit(new MapEdit
			{
				Kind = MapEditKind.Undo,
				UndoGesture = replica.Predicted.LastUndo(replica.Actor)
			});
		}

		internal void ClearAnnotations()
		{
			Submit(new MapEdit
			{
				Kind = MapEditKind.ClearAnnotations
			});
		}

		private bool Submit(MapEdit edit)
		{
			if (!CanEdit)
			{
				return false;
			}
			edit.Sequence = sequence + 1;
			if (!replica.Propose(edit, Now))
			{
				Refresh();
				return false;
			}
			sequence++;
			Refresh();
			if (personal || ZNet.instance.IsServer())
			{
				ProcessEdit(null, edit);
			}
			else
			{
				outgoing.Add(edit);
				inflight.Add(edit.Id, edit);
			}
			return true;
		}

		private void ProcessEdit(Peer peer, MapEdit edit)
		{
			long num = peer?.Actor ?? replica.Actor;
			if (owner == null || ownerFailed)
			{
				Reject(peer, edit.Id