Decompiled source of Waybound v0.2.0

plugins/Ravenbond/Ravenbond.Core.dll

Decompiled 3 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
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 Microsoft.CodeAnalysis;

[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("Rinor")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.0.0")]
[assembly: AssemblyInformationalVersion("0.2.0")]
[assembly: AssemblyProduct("Waybound")]
[assembly: AssemblyTitle("Ravenbond.Core")]
[assembly: AssemblyVersion("0.2.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Ravenbond.Core
{
	public sealed class ExplorationMap
	{
		public const int DefaultSize = 2048;

		public const int ChunkBytes = 8192;

		public const int MaximumPixelCount = 16777216;

		private static readonly byte[] PopCounts = BuildPopCounts();

		private readonly byte[] _bits;

		private readonly long[] _revisions;

		public int Width { get; }

		public int Height { get; }

		public int PixelCount { get; }

		public int ByteLength => _bits.Length;

		public int ChunkCount => _revisions.Length;

		public int ExploredCount { get; private set; }

		public ExplorationMap(int width = 2048, int height = 2048)
		{
			if (width < 1 || width > 8192)
			{
				throw new ArgumentOutOfRangeException("width");
			}
			if (height < 1 || height > 8192 || (long)width * (long)height > 16777216)
			{
				throw new ArgumentOutOfRangeException("height");
			}
			Width = width;
			Height = height;
			PixelCount = checked(width * height);
			_bits = new byte[(PixelCount + 7) / 8];
			_revisions = new long[(_bits.Length + 8192 - 1) / 8192];
		}

		public bool IsExplored(int pixelIndex)
		{
			ValidatePixel(pixelIndex);
			return (_bits[pixelIndex >> 3] & (1 << (pixelIndex & 7))) != 0;
		}

		public bool Reveal(int pixelIndex)
		{
			ValidatePixel(pixelIndex);
			int num = pixelIndex >> 3;
			byte b = (byte)(1 << (pixelIndex & 7));
			if ((_bits[num] & b) != 0)
			{
				return false;
			}
			_bits[num] |= b;
			ExploredCount++;
			_revisions[num / 8192]++;
			return true;
		}

		public bool Reveal(int x, int y)
		{
			if ((uint)x >= (uint)Width)
			{
				throw new ArgumentOutOfRangeException("x");
			}
			if ((uint)y >= (uint)Height)
			{
				throw new ArgumentOutOfRangeException("y");
			}
			return Reveal(y * Width + x);
		}

		public long GetChunkRevision(int chunkIndex)
		{
			ValidateChunk(chunkIndex);
			return _revisions[chunkIndex];
		}

		public int GetChunkByteLength(int chunkIndex)
		{
			ValidateChunk(chunkIndex);
			return Math.Min(8192, _bits.Length - chunkIndex * 8192);
		}

		public byte[] CopyChunk(int chunkIndex)
		{
			byte[] array = new byte[GetChunkByteLength(chunkIndex)];
			Buffer.BlockCopy(_bits, chunkIndex * 8192, array, 0, array.Length);
			return array;
		}

		public byte[] CopyBytes()
		{
			return (byte[])_bits.Clone();
		}

		public bool MergeChunk(int chunkIndex, byte[] data)
		{
			if (data == null)
			{
				throw new ArgumentNullException("data");
			}
			int chunkByteLength = GetChunkByteLength(chunkIndex);
			if (data.Length != chunkByteLength)
			{
				throw new ArgumentException("Unexpected exploration chunk length.", "data");
			}
			if (chunkIndex == ChunkCount - 1 && (PixelCount & 7) != 0 && data[^1] >> (PixelCount & 7) != 0)
			{
				throw new ArgumentException("Exploration chunk contains pixels outside the map.", "data");
			}
			int num = chunkIndex * 8192;
			int num2 = 0;
			for (int i = 0; i < chunkByteLength; i++)
			{
				byte b = _bits[num + i];
				byte b2 = (byte)(data[i] & ~b);
				if (b2 != 0)
				{
					_bits[num + i] = (byte)(b | b2);
					num2 += PopCounts[b2];
				}
			}
			if (num2 == 0)
			{
				return false;
			}
			ExploredCount += num2;
			_revisions[chunkIndex]++;
			return true;
		}

		public int ImportExplored(bool[] source, ref int cursor, int pixelBudget)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			int num = ValidateScan(source.Length, cursor, pixelBudget);
			int num2 = 0;
			for (int i = 0; i < num; i++)
			{
				if (source[cursor] && Reveal(cursor))
				{
					num2++;
				}
				if (++cursor == PixelCount)
				{
					cursor = 0;
				}
			}
			return num2;
		}

		public int ImportExplored(BitArray source, ref int cursor, int pixelBudget)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			int num = ValidateScan(source.Length, cursor, pixelBudget);
			int num2 = 0;
			for (int i = 0; i < num; i++)
			{
				if (source[cursor] && Reveal(cursor))
				{
					num2++;
				}
				if (++cursor == PixelCount)
				{
					cursor = 0;
				}
			}
			return num2;
		}

		public int ApplyTo(bool[] destination, ref int cursor, int pixelBudget)
		{
			if (destination == null)
			{
				throw new ArgumentNullException("destination");
			}
			int num = ValidateScan(destination.Length, cursor, pixelBudget);
			int num2 = 0;
			for (int i = 0; i < num; i++)
			{
				if (!destination[cursor] && IsExplored(cursor))
				{
					destination[cursor] = true;
					num2++;
				}
				if (++cursor == PixelCount)
				{
					cursor = 0;
				}
			}
			return num2;
		}

		public int ApplyTo(BitArray destination, ref int cursor, int pixelBudget)
		{
			if (destination == null)
			{
				throw new ArgumentNullException("destination");
			}
			int num = ValidateScan(destination.Length, cursor, pixelBudget);
			int num2 = 0;
			for (int i = 0; i < num; i++)
			{
				if (!destination[cursor] && IsExplored(cursor))
				{
					destination[cursor] = true;
					num2++;
				}
				if (++cursor == PixelCount)
				{
					cursor = 0;
				}
			}
			return num2;
		}

		public static ExplorationMap FromBytes(int width, int height, byte[] data)
		{
			if (data == null)
			{
				throw new ArgumentNullException("data");
			}
			ExplorationMap explorationMap = new ExplorationMap(width, height);
			if (data.Length != explorationMap.ByteLength)
			{
				throw new ArgumentException("Unexpected exploration bitmap length.", "data");
			}
			for (int i = 0; i < explorationMap.ChunkCount; i++)
			{
				byte[] array = new byte[explorationMap.GetChunkByteLength(i)];
				Buffer.BlockCopy(data, i * 8192, array, 0, array.Length);
				explorationMap.MergeChunk(i, array);
			}
			return explorationMap;
		}

		private int ValidateScan(int length, int cursor, int budget)
		{
			if (length != PixelCount)
			{
				throw new ArgumentException("Native exploration dimensions do not match this world map.");
			}
			if ((uint)cursor >= (uint)PixelCount)
			{
				throw new ArgumentOutOfRangeException("cursor");
			}
			if (budget < 0)
			{
				throw new ArgumentOutOfRangeException("budget");
			}
			return Math.Min(budget, PixelCount);
		}

		private void ValidatePixel(int index)
		{
			if ((uint)index >= (uint)PixelCount)
			{
				throw new ArgumentOutOfRangeException("index");
			}
		}

		private void ValidateChunk(int index)
		{
			if ((uint)index >= (uint)ChunkCount)
			{
				throw new ArgumentOutOfRangeException("index");
			}
		}

		private static byte[] BuildPopCounts()
		{
			byte[] array = new byte[256];
			for (int i = 1; i < array.Length; i++)
			{
				array[i] = (byte)(array[i >> 1] + (i & 1));
			}
			return array;
		}
	}
	public sealed class SharedPin
	{
		public long Id;

		public long OwnerId;

		public long Revision;

		public int Type;

		public int ColorIndex;

		public string Name = "";

		public string OwnerName = "";

		public float X;

		public float Y;

		public float Z;

		public bool Checked;

		public bool Deleted;

		public SharedPin Clone()
		{
			return (SharedPin)MemberwiseClone();
		}
	}
	public sealed class PinEdit
	{
		public long Id;

		public long ExpectedRevision;

		public int Type;

		public string Name = "";

		public float X;

		public float Y;

		public float Z;

		public bool Checked;

		public bool Deleted;
	}
	public static class SharedPinRules
	{
		public const int MaximumRecords = 4096;

		public const int MaximumOwners = 4096;

		public const int MaximumBatch = 32;

		public static bool ValidType(int type)
		{
			if (type != 0 && type != 1 && type != 2 && type != 3 && type != 4 && type != 6 && type != 9 && type != 14 && type != 15)
			{
				return type == 16;
			}
			return true;
		}

		public static bool ValidCoordinate(float value)
		{
			if (!float.IsNaN(value) && !float.IsInfinity(value))
			{
				return Math.Abs(value) <= 100000f;
			}
			return false;
		}

		public static string CleanName(string? value)
		{
			StringBuilder stringBuilder = new StringBuilder(48);
			string text = value ?? "";
			foreach (char c in text)
			{
				if (stringBuilder.Length == 48)
				{
					break;
				}
				if (!char.IsControl(c) && !char.IsSurrogate(c) && c != '<' && c != '>' && c != '$')
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.ToString().Trim();
		}

		public static bool ValidEdit(PinEdit? edit)
		{
			if (edit != null && edit.Id > 0 && edit.ExpectedRevision >= 0 && ValidType(edit.Type) && ValidCoordinate(edit.X) && ValidCoordinate(edit.Y))
			{
				return ValidCoordinate(edit.Z);
			}
			return false;
		}

		public static bool ValidRecord(SharedPin? pin)
		{
			if (pin != null && pin.Id > 0 && pin.OwnerId != 0L && pin.Revision > 0 && pin.ColorIndex >= 0 && pin.ColorIndex < 4096 && ValidType(pin.Type) && ValidCoordinate(pin.X) && ValidCoordinate(pin.Y) && ValidCoordinate(pin.Z) && pin.Name == CleanName(pin.Name))
			{
				return pin.OwnerName == CleanName(pin.OwnerName);
			}
			return false;
		}
	}
	public sealed class SharedPinBoard
	{
		private readonly Dictionary<long, SharedPin> _records = new Dictionary<long, SharedPin>();

		private readonly Dictionary<long, int> _colors = new Dictionary<long, int>();

		public long Revision { get; private set; }

		public int Count { get; private set; }

		public IReadOnlyCollection<SharedPin> Records => new ReadOnlyCollection<SharedPin>(_records.Values.Select((SharedPin pin) => pin.Clone()).ToList());

		internal IEnumerable<KeyValuePair<long, int>> Colors => _colors;

		public int GetColorIndex(long ownerId)
		{
			if (ownerId == 0L)
			{
				throw new ArgumentOutOfRangeException("ownerId");
			}
			if (_colors.TryGetValue(ownerId, out var value))
			{
				return value;
			}
			if (_colors.Count >= 4096 || Revision == long.MaxValue)
			{
				throw new InvalidOperationException("The shared pin owner limit has been reached.");
			}
			int count = _colors.Count;
			_colors.Add(ownerId, count);
			Revision++;
			return count;
		}

		public bool TryApply(PinEdit edit, long ownerId, string ownerName, out SharedPin? current)
		{
			current = null;
			if (edit == null)
			{
				return false;
			}
			_records.TryGetValue(edit.Id, out SharedPin value);
			current = value?.Clone();
			if (ownerId == 0L || !SharedPinRules.ValidEdit(edit) || Revision == long.MaxValue)
			{
				return false;
			}
			if (value == null)
			{
				if (edit.ExpectedRevision != 0L || edit.Deleted || _records.Count >= 4096 || (!_colors.ContainsKey(ownerId) && (_colors.Count >= 4096 || Revision >= 9223372036854775806L)))
				{
					return false;
				}
				int colorIndex = GetColorIndex(ownerId);
				SharedPin sharedPin = new SharedPin
				{
					Id = edit.Id,
					OwnerId = ownerId,
					OwnerName = SharedPinRules.CleanName(ownerName),
					ColorIndex = colorIndex
				};
				CopyEdit(edit, sharedPin);
				sharedPin.Revision = ++Revision;
				_records.Add(sharedPin.Id, sharedPin);
				Count++;
				current = sharedPin.Clone();
				return true;
			}
			if (value.Deleted || edit.ExpectedRevision != value.Revision)
			{
				return false;
			}
			if (edit.Deleted)
			{
				value.Deleted = true;
				Count--;
			}
			else
			{
				string text = SharedPinRules.CleanName(edit.Name);
				if (value.Name == text && value.Type == edit.Type && value.X == edit.X && value.Y == edit.Y && value.Z == edit.Z && value.Checked == edit.Checked)
				{
					return true;
				}
				CopyEdit(edit, value);
			}
			value.Revision = ++Revision;
			current = value.Clone();
			return true;
		}

		public IReadOnlyList<SharedPin> ChangesAfter(long revision, int maximum = 32)
		{
			if (revision < 0)
			{
				throw new ArgumentOutOfRangeException("revision");
			}
			if (maximum < 1 || maximum > 32)
			{
				throw new ArgumentOutOfRangeException("maximum");
			}
			return new ReadOnlyCollection<SharedPin>((from pin in (from pin in _records.Values
					where pin.Revision > revision
					orderby pin.Revision
					select pin).Take(maximum)
				select pin.Clone()).ToList());
		}

		public SharedPinBoard Clone()
		{
			SharedPinBoard sharedPinBoard = new SharedPinBoard
			{
				Revision = Revision,
				Count = Count
			};
			foreach (KeyValuePair<long, int> color in _colors)
			{
				sharedPinBoard._colors.Add(color.Key, color.Value);
			}
			foreach (KeyValuePair<long, SharedPin> record in _records)
			{
				sharedPinBoard._records.Add(record.Key, record.Value.Clone());
			}
			return sharedPinBoard;
		}

		internal static SharedPinBoard Restore(long revision, IEnumerable<KeyValuePair<long, int>> colors, IEnumerable<SharedPin> records)
		{
			if (revision < 0)
			{
				throw new ArgumentException("Invalid board revision.");
			}
			SharedPinBoard sharedPinBoard = new SharedPinBoard
			{
				Revision = revision
			};
			HashSet<int> hashSet = new HashSet<int>();
			foreach (KeyValuePair<long, int> color in colors)
			{
				if (sharedPinBoard._colors.Count >= 4096 || color.Key == 0L || color.Value < 0 || color.Value >= 4096 || sharedPinBoard._colors.ContainsKey(color.Key) || !hashSet.Add(color.Value))
				{
					throw new ArgumentException("Invalid owner color assignment.");
				}
				sharedPinBoard._colors.Add(color.Key, color.Value);
			}
			for (int i = 0; i < hashSet.Count; i++)
			{
				if (!hashSet.Contains(i))
				{
					throw new ArgumentException("Non-consecutive owner colors.");
				}
			}
			HashSet<long> hashSet2 = new HashSet<long>();
			foreach (SharedPin record in records)
			{
				if (sharedPinBoard._records.Count >= 4096 || !SharedPinRules.ValidRecord(record) || record.Revision > revision || sharedPinBoard._records.ContainsKey(record.Id) || !hashSet2.Add(record.Revision) || !sharedPinBoard._colors.TryGetValue(record.OwnerId, out var value) || value != record.ColorIndex)
				{
					throw new ArgumentException("Invalid shared pin record.");
				}
				sharedPinBoard._records.Add(record.Id, record.Clone());
				if (!record.Deleted)
				{
					sharedPinBoard.Count++;
				}
			}
			if (revision < sharedPinBoard._colors.Count + sharedPinBoard._records.Count)
			{
				throw new ArgumentException("Board revision precedes its state.");
			}
			return sharedPinBoard;
		}

		private static void CopyEdit(PinEdit edit, SharedPin pin)
		{
			pin.Name = SharedPinRules.CleanName(edit.Name);
			pin.Type = edit.Type;
			pin.X = edit.X;
			pin.Y = edit.Y;
			pin.Z = edit.Z;
			pin.Checked = edit.Checked;
		}
	}
	public sealed class SharedPinLoadResult
	{
		public SharedPinBoard Board { get; }

		public ExplorationLoadSource Source { get; }

		public string? Warning { get; }

		public bool RecoveredFromBackup => Source == ExplorationLoadSource.Backup;

		internal SharedPinLoadResult(SharedPinBoard board, ExplorationLoadSource source, string? warning)
		{
			Board = board;
			Source = source;
			Warning = warning;
		}
	}
	public sealed class SharedPinStore
	{
		private const int FormatVersion = 1;

		private const int MaximumBytes = 2097152;

		private static readonly byte[] Magic = Encoding.ASCII.GetBytes("RBPINS01");

		private static readonly UTF8Encoding Utf8 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);

		private readonly string _directory;

		public SharedPinStore(string directory)
		{
			if (string.IsNullOrWhiteSpace(directory))
			{
				throw new ArgumentException("A save directory is required.", "directory");
			}
			_directory = Path.GetFullPath(directory);
		}

		public string GetPath(long worldId)
		{
			return Path.Combine(_directory, "world-" + worldId.ToString("X16", CultureInfo.InvariantCulture) + ".rbpins");
		}

		public SharedPinLoadResult Load(long worldId)
		{
			string path = GetPath(worldId);
			if (TryRead(path, worldId, out SharedPinBoard board, out string error))
			{
				return new SharedPinLoadResult(board, ExplorationLoadSource.Primary, null);
			}
			if (TryRead(path + ".bak", worldId, out board, out string error2))
			{
				return new SharedPinLoadResult(board, ExplorationLoadSource.Backup, "Recovered shared pins from backup. " + (error ?? "Primary snapshot was missing."));
			}
			return new SharedPinLoadResult(new SharedPinBoard(), ExplorationLoadSource.Empty, (error == null && error2 == null) ? null : ("Shared pin storage could not be loaded; existing files must be preserved. " + error + " " + error2));
		}

		public void Save(long worldId, SharedPinBoard board)
		{
			if (board == null)
			{
				throw new ArgumentNullException("board");
			}
			Directory.CreateDirectory(_directory);
			string path = GetPath(worldId);
			string text = path + ".bak";
			bool flag = File.Exists(path);
			bool flag2 = File.Exists(text);
			SharedPinBoard board2;
			string error;
			bool flag3 = TryRead(path, worldId, out board2, out error);
			if (!flag3 && (flag || flag2) && !TryRead(text, worldId, out board2, out error))
			{
				throw new InvalidDataException("Existing shared pin snapshots cannot be verified; refusing to overwrite them.");
			}
			byte[] array = Encode(worldId, board);
			string text2 = path + "." + Guid.NewGuid().ToString("N") + ".tmp";
			try
			{
				using (FileStream fileStream = new FileStream(text2, FileMode.CreateNew, FileAccess.Write, FileShare.None))
				{
					fileStream.Write(array, 0, array.Length);
					fileStream.Flush(flushToDisk: true);
				}
				if (flag)
				{
					File.Replace(text2, path, flag3 ? text : null, ignoreMetadataErrors: true);
				}
				else
				{
					File.Move(text2, path);
				}
			}
			finally
			{
				if (File.Exists(text2))
				{
					File.Delete(text2);
				}
			}
		}

		private static byte[] Encode(long worldId, SharedPinBoard board)
		{
			using MemoryStream memoryStream = new MemoryStream();
			using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Utf8, leaveOpen: true))
			{
				binaryWriter.Write(Magic);
				binaryWriter.Write(1);
				binaryWriter.Write(worldId);
				binaryWriter.Write(board.Revision);
				KeyValuePair<long, int>[] array = board.Colors.OrderBy((KeyValuePair<long, int> pair) => pair.Value).ToArray();
				binaryWriter.Write(array.Length);
				KeyValuePair<long, int>[] array2 = array;
				for (int num = 0; num < array2.Length; num++)
				{
					KeyValuePair<long, int> keyValuePair = array2[num];
					binaryWriter.Write(keyValuePair.Key);
					binaryWriter.Write(keyValuePair.Value);
				}
				SharedPin[] array3 = board.Records.OrderBy((SharedPin pin) => pin.Id).ToArray();
				binaryWriter.Write(array3.Length);
				SharedPin[] array4 = array3;
				foreach (SharedPin sharedPin in array4)
				{
					binaryWriter.Write(sharedPin.Id);
					binaryWriter.Write(sharedPin.OwnerId);
					binaryWriter.Write(sharedPin.Revision);
					binaryWriter.Write(sharedPin.Type);
					binaryWriter.Write(sharedPin.ColorIndex);
					WriteName(binaryWriter, sharedPin.Name);
					WriteName(binaryWriter, sharedPin.OwnerName);
					binaryWriter.Write(sharedPin.X);
					binaryWriter.Write(sharedPin.Y);
					binaryWriter.Write(sharedPin.Z);
					binaryWriter.Write(sharedPin.Checked);
					binaryWriter.Write(sharedPin.Deleted);
				}
				binaryWriter.Flush();
			}
			using (SHA256 sHA = SHA256.Create())
			{
				byte[] array5 = sHA.ComputeHash(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
				memoryStream.Write(array5, 0, array5.Length);
			}
			return memoryStream.ToArray();
		}

		private static void WriteName(BinaryWriter writer, string value)
		{
			byte[] bytes = Utf8.GetBytes(value);
			writer.Write((ushort)bytes.Length);
			writer.Write(bytes);
		}

		private static string ReadName(BinaryReader reader)
		{
			int num = reader.ReadUInt16();
			if (num > 144)
			{
				throw new InvalidDataException("Pin name exceeds its limit.");
			}
			byte[] array = reader.ReadBytes(num);
			if (array.Length != num)
			{
				throw new EndOfStreamException();
			}
			string text = Utf8.GetString(array);
			if (text != SharedPinRules.CleanName(text))
			{
				throw new InvalidDataException("Invalid pin name.");
			}
			return text;
		}

		private static bool ReadBool(BinaryReader reader)
		{
			byte num = reader.ReadByte();
			if (num > 1)
			{
				throw new InvalidDataException("Invalid boolean.");
			}
			return num == 1;
		}

		private static bool TryRead(string path, long worldId, out SharedPinBoard? board, out string? error)
		{
			board = null;
			error = null;
			try
			{
				if (!File.Exists(path))
				{
					return false;
				}
				byte[] array;
				using (FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
				{
					if (fileStream.Length < 68 || fileStream.Length > 2097152)
					{
						throw new InvalidDataException("Invalid pin snapshot length.");
					}
					array = new byte[(int)fileStream.Length];
					int num;
					for (int i = 0; i < array.Length; i += num)
					{
						num = fileStream.Read(array, i, array.Length - i);
						if (num == 0)
						{
							throw new EndOfStreamException();
						}
					}
				}
				using (SHA256 sHA = SHA256.Create())
				{
					byte[] array2 = sHA.ComputeHash(array, 0, array.Length - 32);
					int num2 = 0;
					for (int j = 0; j < 32; j++)
					{
						num2 |= array2[j] ^ array[array.Length - 32 + j];
					}
					if (num2 != 0)
					{
						throw new InvalidDataException("Pin snapshot checksum does not match.");
					}
				}
				using (BinaryReader binaryReader = new BinaryReader(new MemoryStream(array, 0, array.Length - 32, writable: false), Utf8, leaveOpen: false))
				{
					byte[] magic = Magic;
					foreach (byte b in magic)
					{
						if (binaryReader.ReadByte() != b)
						{
							throw new InvalidDataException("Unknown pin snapshot signature.");
						}
					}
					if (binaryReader.ReadInt32() != 1)
					{
						throw new InvalidDataException("Unsupported pin snapshot version.");
					}
					if (binaryReader.ReadInt64() != worldId)
					{
						throw new InvalidDataException("Pin snapshot belongs to another world.");
					}
					long revision = binaryReader.ReadInt64();
					int num3 = binaryReader.ReadInt32();
					if (num3 < 0 || num3 > 4096)
					{
						throw new InvalidDataException("Invalid pin owner count.");
					}
					List<KeyValuePair<long, int>> list = new List<KeyValuePair<long, int>>(num3);
					for (int l = 0; l < num3; l++)
					{
						list.Add(new KeyValuePair<long, int>(binaryReader.ReadInt64(), binaryReader.ReadInt32()));
					}
					int num4 = binaryReader.ReadInt32();
					if (num4 < 0 || num4 > 4096)
					{
						throw new InvalidDataException("Invalid pin count.");
					}
					List<SharedPin> list2 = new List<SharedPin>(num4);
					for (int m = 0; m < num4; m++)
					{
						list2.Add(new SharedPin
						{
							Id = binaryReader.ReadInt64(),
							OwnerId = binaryReader.ReadInt64(),
							Revision = binaryReader.ReadInt64(),
							Type = binaryReader.ReadInt32(),
							ColorIndex = binaryReader.ReadInt32(),
							Name = ReadName(binaryReader),
							OwnerName = ReadName(binaryReader),
							X = binaryReader.ReadSingle(),
							Y = binaryReader.ReadSingle(),
							Z = binaryReader.ReadSingle(),
							Checked = ReadBool(binaryReader),
							Deleted = ReadBool(binaryReader)
						});
					}
					if (binaryReader.BaseStream.Position != binaryReader.BaseStream.Length)
					{
						throw new InvalidDataException("Trailing pin snapshot data.");
					}
					board = SharedPinBoard.Restore(revision, list, list2);
				}
				return true;
			}
			catch (Exception ex) when (ex is InvalidDataException || ex is IOException || ex is UnauthorizedAccessException || ex is ArgumentException)
			{
				error = Path.GetFileName(path) + ": " + ex.Message;
				return false;
			}
		}
	}
	public enum ExplorationLoadSource
	{
		Empty,
		Primary,
		Backup
	}
	public sealed class ExplorationLoadResult
	{
		public ExplorationMap Map { get; }

		public ExplorationLoadSource Source { get; }

		public bool RecoveredFromBackup => Source == ExplorationLoadSource.Backup;

		public string? Warning { get; }

		internal ExplorationLoadResult(ExplorationMap map, ExplorationLoadSource source, string? warning)
		{
			Map = map;
			Source = source;
			Warning = warning;
		}
	}
	public sealed class WorldExplorationStore
	{
		private const int FormatVersion = 1;

		private const int HeaderBytes = 32;

		private const int HashBytes = 32;

		private static readonly byte[] Magic = Encoding.ASCII.GetBytes("RAVENBND");

		private readonly string _directory;

		public WorldExplorationStore(string directory)
		{
			if (string.IsNullOrWhiteSpace(directory))
			{
				throw new ArgumentException("A save directory is required.", "directory");
			}
			_directory = Path.GetFullPath(directory);
		}

		public string GetPath(long worldId)
		{
			return Path.Combine(_directory, "world-" + worldId.ToString("X16", CultureInfo.InvariantCulture) + ".rbmap");
		}

		public ExplorationLoadResult Load(long worldId, int width = 2048, int height = 2048)
		{
			ExplorationMap map = new ExplorationMap(width, height);
			string path = GetPath(worldId);
			if (TryRead(path, worldId, width, height, out ExplorationMap map2, out string error))
			{
				return new ExplorationLoadResult(map2, ExplorationLoadSource.Primary, null);
			}
			if (TryRead(path + ".bak", worldId, width, height, out ExplorationMap map3, out string error2))
			{
				return new ExplorationLoadResult(map3, ExplorationLoadSource.Backup, "Recovered shared exploration from backup. " + (error ?? "Primary snapshot was missing."));
			}
			string warning = ((error == null && error2 == null) ? null : ("Shared exploration could not be loaded; native player exploration is unaffected. " + error + " " + error2));
			return new ExplorationLoadResult(map, ExplorationLoadSource.Empty, warning);
		}

		public void Save(long worldId, ExplorationMap map)
		{
			if (map == null)
			{
				throw new ArgumentNullException("map");
			}
			Directory.CreateDirectory(_directory);
			string path = GetPath(worldId);
			string text = path + "." + Guid.NewGuid().ToString("N") + ".tmp";
			byte[] array = Encode(worldId, map);
			try
			{
				using (FileStream fileStream = new FileStream(text, FileMode.CreateNew, FileAccess.Write, FileShare.None))
				{
					fileStream.Write(array, 0, array.Length);
					fileStream.Flush(flushToDisk: true);
				}
				if (File.Exists(path))
				{
					ExplorationMap map2;
					string error;
					bool flag = TryRead(path, worldId, map.Width, map.Height, out map2, out error);
					File.Replace(text, path, flag ? (path + ".bak") : null, ignoreMetadataErrors: true);
				}
				else
				{
					File.Move(text, path);
				}
			}
			finally
			{
				if (File.Exists(text))
				{
					File.Delete(text);
				}
			}
		}

		private static byte[] Encode(long worldId, ExplorationMap map)
		{
			byte[] array = map.CopyBytes();
			using MemoryStream memoryStream = new MemoryStream(32 + array.Length + 32);
			using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8, leaveOpen: true))
			{
				binaryWriter.Write(Magic);
				binaryWriter.Write(1);
				binaryWriter.Write(worldId);
				binaryWriter.Write(map.Width);
				binaryWriter.Write(map.Height);
				binaryWriter.Write(array.Length);
				binaryWriter.Write(array);
				binaryWriter.Flush();
			}
			using (SHA256 sHA = SHA256.Create())
			{
				byte[] array2 = sHA.ComputeHash(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
				memoryStream.Write(array2, 0, array2.Length);
			}
			return memoryStream.ToArray();
		}

		private static bool TryRead(string path, long worldId, int width, int height, out ExplorationMap? map, out string? error)
		{
			map = null;
			error = null;
			try
			{
				if (!File.Exists(path))
				{
					return false;
				}
				int num = checked(width * height + 7) / 8;
				byte[] array;
				using (FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
				{
					if (fileStream.Length != 32 + num + 32)
					{
						throw new InvalidDataException("Snapshot length or map dimensions do not match.");
					}
					array = new byte[(int)fileStream.Length];
					int num2;
					for (int i = 0; i < array.Length; i += num2)
					{
						num2 = fileStream.Read(array, i, array.Length - i);
						if (num2 == 0)
						{
							throw new EndOfStreamException("Snapshot was truncated.");
						}
					}
				}
				using (SHA256 sHA = SHA256.Create())
				{
					byte[] array2 = sHA.ComputeHash(array, 0, array.Length - 32);
					int num3 = 0;
					for (int j = 0; j < 32; j++)
					{
						num3 |= array2[j] ^ array[array.Length - 32 + j];
					}
					if (num3 != 0)
					{
						throw new InvalidDataException("Snapshot checksum does not match.");
					}
				}
				using (BinaryReader binaryReader = new BinaryReader(new MemoryStream(array, writable: false), Encoding.UTF8, leaveOpen: false))
				{
					for (int k = 0; k < Magic.Length; k++)
					{
						if (binaryReader.ReadByte() != Magic[k])
						{
							throw new InvalidDataException("Unknown snapshot signature.");
						}
					}
					if (binaryReader.ReadInt32() != 1)
					{
						throw new InvalidDataException("Unsupported snapshot version.");
					}
					if (binaryReader.ReadInt64() != worldId)
					{
						throw new InvalidDataException("Snapshot belongs to a different world.");
					}
					if (binaryReader.ReadInt32() != width || binaryReader.ReadInt32() != height)
					{
						throw new InvalidDataException("Snapshot map dimensions do not match.");
					}
					if (binaryReader.ReadInt32() != num)
					{
						throw new InvalidDataException("Snapshot bitmap length does not match.");
					}
					map = ExplorationMap.FromBytes(width, height, binaryReader.ReadBytes(num));
				}
				return true;
			}
			catch (Exception ex) when (ex is InvalidDataException || ex is IOException || ex is UnauthorizedAccessException || ex is ArgumentException)
			{
				error = Path.GetFileName(path) + ": " + ex.Message;
				return false;
			}
		}
	}
}

plugins/Ravenbond/Ravenbond.dll

Decompiled 3 weeks ago
using System;
using System.Collections;
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.Text;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using Ravenbond.Core;
using Ravenbond.Networking;
using Ravenbond.UI;
using Splatform;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Rinor")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Waybound: shared exploration and smooth friend presence for private Valheim worlds.")]
[assembly: AssemblyFileVersion("0.2.0.0")]
[assembly: AssemblyInformationalVersion("0.2.0")]
[assembly: AssemblyProduct("Waybound")]
[assembly: AssemblyTitle("Ravenbond")]
[assembly: AssemblyVersion("0.2.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Ravenbond
{
	internal sealed class MapBridge
	{
		private static readonly FieldRef<Minimap, BitArray> Local = AccessTools.FieldRefAccess<Minimap, BitArray>("m_explored");

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

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

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

		private static readonly FieldRef<Minimap, int> Size = AccessTools.FieldRefAccess<Minimap, int>("m_textureSize");

		private readonly Stopwatch _watch = new Stopwatch();

		private Minimap? _boundMap;

		private Texture2D? _boundFog;

		private int _cursor;

		private bool _textureDirty;

		private float _nextApply;

		internal static bool Ready(Minimap map)
		{
			if (Object.op_Implicit((Object)(object)map) && Generated.Invoke(map))
			{
				return Size.Invoke(map) == 2048;
			}
			return false;
		}

		internal void Tick(ExplorationMap map, float budgetMs, float now)
		{
			if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				TickNative(Minimap.instance, map, budgetMs, now);
			}
		}

		internal void TickNative(Minimap minimap, ExplorationMap map, float budgetMs, float now)
		{
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			if (!Ready(minimap))
			{
				return;
			}
			BitArray bitArray = Local.Invoke(minimap);
			BitArray bitArray2 = Shared.Invoke(minimap);
			Texture2D val = Fog.Invoke(minimap);
			if (bitArray == null || bitArray2 == null || !Object.op_Implicit((Object)(object)val) || bitArray.Length != map.PixelCount || bitArray2.Length != map.PixelCount)
			{
				return;
			}
			if ((Object)(object)_boundMap != (Object)(object)minimap || (Object)(object)_boundFog != (Object)(object)val)
			{
				_boundMap = minimap;
				_boundFog = val;
				_cursor = 0;
				_textureDirty = false;
				_nextApply = 0f;
			}
			_watch.Restart();
			for (int i = 0; i < 32768; i++)
			{
				int cursor = _cursor;
				_cursor = (_cursor + 1) % map.PixelCount;
				if (bitArray[cursor] || bitArray2[cursor])
				{
					map.Reveal(cursor);
				}
				if (!bitArray[cursor] && map.IsExplored(cursor))
				{
					int num = cursor % 2048;
					int num2 = cursor / 2048;
					Color pixel = val.GetPixel(num, num2);
					pixel.r = 0f;
					val.SetPixel(num, num2, pixel);
					bitArray[cursor] = true;
					_textureDirty = true;
				}
				if ((i & 0xFF) == 255 && _watch.Elapsed.TotalMilliseconds >= (double)budgetMs)
				{
					break;
				}
			}
			if (_textureDirty && now >= _nextApply)
			{
				val.Apply(false, false);
				_textureDirty = false;
				_nextApply = now + 0.25f;
			}
		}

		internal void Flush()
		{
			if (_textureDirty && Object.op_Implicit((Object)(object)_boundFog))
			{
				_boundFog.Apply(false, false);
			}
			_textureDirty = false;
		}
	}
	[BepInPlugin("rinor.ravenbond", "Waybound", "0.2.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		private sealed class PeerState
		{
			internal long Client;

			internal long Owner;

			internal long PositionSequence;

			internal ZNetPeer Connection;

			internal readonly ChunkChannel Channel = new ChunkChannel(64);

			internal readonly PinChannel Pins = new PinChannel();

			internal long PinOperation;

			internal Packet? PinResult;

			internal Presence? Presence;

			internal float Seen;

			internal float LastHello;

			internal float Window;

			internal int Packets;
		}

		[HarmonyPatch(typeof(ZNet), "Shutdown", new Type[] { typeof(bool) })]
		private static class ShutdownPatch
		{
			private static void Prefix()
			{
				Instance?.EndSession();
			}
		}

		[HarmonyPatch(typeof(Minimap), "Explore", new Type[]
		{
			typeof(int),
			typeof(int)
		})]
		private static class ExplorePatch
		{
			private static void Postfix(Minimap __instance, int x, int y, bool __result)
			{
				if (!__result || !MapBridge.Ready(__instance) || x < 0 || x >= 2048 || y < 0 || y >= 2048)
				{
					return;
				}
				Plugin? instance = Instance;
				if (instance != null)
				{
					ExplorationMap? map = instance._map;
					if (map != null)
					{
						map.Reveal(y * 2048 + x);
					}
				}
			}
		}

		[HarmonyPatch(typeof(Minimap), "UpdatePlayerPins", new Type[] { typeof(float) })]
		private static class NativePinsPatch
		{
			private static bool Prefix(Minimap __instance, List<PinData> ___m_playerPins)
			{
				Plugin? instance = Instance;
				if (instance == null || !instance.OwnMapPins)
				{
					return true;
				}
				foreach (PinData ___m_playerPin in ___m_playerPins)
				{
					__instance.RemovePin(___m_playerPin);
				}
				___m_playerPins.Clear();
				return false;
			}
		}

		private sealed class QueuedEdit
		{
			internal PinEdit Edit;

			internal long Generation;
		}

		[HarmonyPatch(typeof(Minimap), "ShowPinNameInput", new Type[] { typeof(Vector3) })]
		private static class ManualPinPatch
		{
			private static void Postfix(Minimap __instance, PinData ___m_namePin)
			{
				Plugin instance = Instance;
				if (instance != null && instance._connected && ___m_namePin != null && Object.op_Implicit((Object)(object)Player.m_localPlayer))
				{
					instance._pinBridge?.Capture(__instance, ___m_namePin, LocalOwner, Player.m_localPlayer.GetPlayerName(), instance._localColorIndex);
				}
			}
		}

		[HarmonyPatch(typeof(Minimap), "RemovePin", new Type[] { typeof(PinData) })]
		private static class RemoveSharedPinPatch
		{
			private static void Postfix(Minimap __instance, PinData pin)
			{
				Instance?._pinBridge?.Removed(__instance, pin);
			}
		}

		[HarmonyPatch(typeof(Minimap), "GetClosestPin", new Type[]
		{
			typeof(Vector3),
			typeof(float),
			typeof(bool)
		})]
		private static class SelectSharedPinPatch
		{
			private static void Postfix(Minimap __instance, Vector3 pos, float radius, bool mustBeVisible, ref PinData __result)
			{
				//IL_0015: Unknown result type (might be due to invalid IL or missing references)
				Instance?._pinBridge?.IncludeClosest(__instance, pos, radius, mustBeVisible, ref __result);
			}
		}

		public const string PluginGuid = "rinor.ravenbond";

		public const string PluginName = "Waybound";

		public const string PluginVersion = "0.2.0";

		internal static Plugin? Instance;

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

		private readonly List<long> _departed = new List<long>();

		private readonly List<FriendView> _friends = new List<FriendView>(16);

		private readonly List<Presence> _roster = new List<Presence>(16);

		private readonly HudSettings _settings = new HudSettings();

		private Harmony _harmony;

		private CustomRPC _rpc;

		private ConfigEntry<float> _range;

		private ConfigEntry<float> _serverRange;

		private ConfigEntry<float> _scale;

		private ConfigEntry<float> _opacity;

		private ConfigEntry<float> _mapBudget;

		private ConfigEntry<bool> _labels;

		private ConfigEntry<bool> _pins;

		private ConfigEntry<bool> _edges;

		private ConfigEntry<bool> _distance;

		private ConfigEntry<bool> _reduced;

		private ConfigEntry<bool> _adaptive;

		private ConfigEntry<KeyboardShortcut> _toggle;

		private ConfigEntry<int> _saveInterval;

		private ExplorationMap? _map;

		private MapBridge? _bridge;

		private WorldExplorationStore? _store;

		private ChunkChannel? _upload;

		private FriendHud? _hud;

		private ZNet? _session;

		private long _world;

		private long _epoch;

		private long _client;

		private long _positionSequence;

		private long _rosterSequence;

		private long _receivedRosterSequence;

		private float _nextHello;

		private float _nextPosition;

		private float _nextNetwork;

		private float _nextRoster;

		private float _nextSave;

		private float _lastRoster;

		private float _hostRange = 2000f;

		private float _nextWarning;

		private bool _connected;

		private int _savedPixels;

		private Task<string?>? _saveTask;

		private int _savingPixels;

		private string _storageStatus = "";

		private readonly Dictionary<long, SharedPin> _knownPins = new Dictionary<long, SharedPin>();

		private readonly Dictionary<long, QueuedEdit> _pinEdits = new Dictionary<long, QueuedEdit>();

		private readonly List<Presence> _legendRoster = new List<Presence>();

		private readonly List<LegendEntry> _legendEntries = new List<LegendEntry>();

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

		private SharedPinBoard? _pinBoard;

		private SharedPinStore? _pinStore;

		private SharedPinBridge? _pinBridge;

		private PlayerLegend? _legend;

		private ConfigEntry<bool> _legendEnabled;

		private Packet? _pendingPin;

		private long _pinGeneration;

		private long _pendingGeneration;

		private long _pinSequence;

		private long _receivedPinRevision;

		private float _nextPinRetry;

		private int _localColorIndex;

		private bool _legendDirty;

		private Task<string?>? _pinSaveTask;

		private long _savedPinRevision;

		private long _savingPinRevision;

		internal bool OwnMapPins
		{
			get
			{
				if (_connected && _pins.Value)
				{
					return Time.unscaledTime - _lastRoster < 5f;
				}
				return false;
			}
		}

		private static long ServerId
		{
			get
			{
				ZNet instance = ZNet.instance;
				return ((instance == null) ? ((long?)null) : instance.GetServerPeer()?.m_uid).GetValueOrDefault();
			}
		}

		private int SharedPinCount
		{
			get
			{
				int num = 0;
				foreach (SharedPin value in _knownPins.Values)
				{
					if (!value.Deleted)
					{
						num++;
					}
				}
				return num;
			}
		}

		private static long LocalOwner => Game.instance.GetPlayerProfile().GetPlayerID();

		private void Awake()
		{
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Expected O, but got Unknown
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			//IL_024d: Expected O, but got Unknown
			//IL_024d: Expected O, but got Unknown
			//IL_0258: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Expected O, but got Unknown
			//IL_028d: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a1: Expected O, but got Unknown
			//IL_029c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c7: Expected O, but got Unknown
			//IL_02c2: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			_range = Float("HUD", "WorldRange", 200f, 25f, 2000f, "World label range in metres, capped by the host. Map pins have no distance limit.");
			_serverRange = Float("Server", "MaximumWorldRange", 2000f, 25f, 2000f, "Maximum permitted world ESP range. Shared with all clients.");
			_scale = Float("HUD", "LabelScale", 1f, 0.65f, 1.6f, "Friend label scale.");
			_opacity = Float("HUD", "Opacity", 0.95f, 0.25f, 1f, "Maximum label opacity.");
			_mapBudget = Float("Performance", "MapWorkMilliseconds", 0.6f, 0.1f, 3f, "CPU scan budget per frame, excluding the coalesced native texture upload.");
			_labels = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "NameESP", false, "Enable or disable friend names and world markers through obstacles. False keeps the world overlay hidden. Map pins and map sharing stay enabled independently. Toggle with F10 or waybound esp on/off.");
			_pins = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "MapPins", true, "Show every connected friend on the map regardless of range or vanilla visibility toggle.");
			_edges = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "EdgeIndicators", true, "Show a direction indicator for friends outside the camera view.");
			_distance = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowDistance", true, "Show metres below names.");
			_reduced = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ReducedMotion", false, "Resolve visibility and map transitions immediately, with reduced caption motion.");
			_adaptive = ((BaseUnityPlugin)this).Config.Bind<bool>("Performance", "AdaptiveQuality", true, "Reduce label/roster work at low FPS while retaining frame-time-based movement.");
			_saveInterval = ((BaseUnityPlugin)this).Config.Bind<int>("Server", "AutosaveSeconds", 30, new ConfigDescription("Shared map autosave interval.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 300), Array.Empty<object>()));
			_toggle = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "ToggleWorldLabels", new KeyboardShortcut((KeyCode)291, Array.Empty<KeyCode>()), "Toggle NameESP without changing map pins or map sharing.");
			_legendEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "PlayerLegend", true, "Show the player color legend in the corner of the large map.");
			_rpc = NetworkManager.Instance.AddRPC("Ravenbond_v1", new CoroutineHandler(ReceiveServer), new CoroutineHandler(ReceiveClient));
			_harmony = new Harmony("rinor.ravenbond");
			_harmony.PatchAll(typeof(Plugin).Assembly);
			new ConsoleCommand("waybound", "Waybound status; waybound reload; waybound esp on/off", new ConsoleEvent(ShowStatus), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			new ConsoleCommand("ravenbond", "Waybound legacy alias; supports reload and esp on/off", new ConsoleEvent(ShowStatus), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Waybound loaded. Install on the server and every client. F10 toggles names. New manual map pins are shared automatically.");
		}

		private void ShowStatus(ConsoleEventArgs args)
		{
			if (args.Args.Length > 1 && string.Equals(args.Args[1], "reload", StringComparison.OrdinalIgnoreCase))
			{
				((BaseUnityPlugin)this).Config.Reload();
				_nextHello = 0f;
			}
			if (args.Args.Length > 1 && string.Equals(args.Args[1], "esp", StringComparison.OrdinalIgnoreCase))
			{
				string a = ((args.Args.Length == 3) ? args.Args[2] : "");
				if (string.Equals(a, "on", StringComparison.OrdinalIgnoreCase) || string.Equals(a, "true", StringComparison.OrdinalIgnoreCase))
				{
					_labels.Value = true;
				}
				else
				{
					if (!string.Equals(a, "off", StringComparison.OrdinalIgnoreCase) && !string.Equals(a, "false", StringComparison.OrdinalIgnoreCase))
					{
						args.Context.AddString("Use waybound esp on or waybound esp off (true/false also accepted).");
						return;
					}
					_labels.Value = false;
				}
			}
			Terminal context = args.Context;
			object[] obj = new object[9]
			{
				"0.2.0",
				ConnectionStatus(),
				_friends.Count,
				null,
				null,
				null,
				null,
				null,
				null
			};
			ExplorationMap? map = _map;
			obj[3] = ((map != null) ? map.ExploredCount : 0);
			obj[4] = SharedPinCount;
			obj[5] = _pinEdits.Count;
			obj[6] = (_labels.Value ? "ON" : "OFF");
			obj[7] = Mathf.Min(_range.Value, _hostRange);
			obj[8] = _storageStatus;
			context.AddString(string.Format("Waybound {0}: {1}; {2} friends; {3} shared pixels; {4} shared pins ({5} pending); Name ESP {6}; range {7:0} m. {8}", obj));
		}

		private string ConnectionStatus()
		{
			if (!Object.op_Implicit((Object)(object)_session))
			{
				return "waiting for world/server";
			}
			if (!_session.IsServer())
			{
				if (!_connected)
				{
					return "waiting for Waybound handshake / character spawn";
				}
				return "connected to host";
			}
			int num = 0;
			int num2 = 0;
			foreach (ZNetPeer peer in _session.GetPeers())
			{
				if (peer.IsReady())
				{
					num++;
					if (_peers.TryGetValue(peer.m_uid, out PeerState value) && value.Connection == peer)
					{
						num2++;
					}
				}
			}
			return $"host; {num} game peers, {num2} Waybound peers";
		}

		private ConfigEntry<float> Float(string section, string key, float value, float min, float max, string description)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			return ((BaseUnityPlugin)this).Config.Bind<float>(section, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), Array.Empty<object>()));
		}

		private void Update()
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			if (!EnsureSession())
			{
				return;
			}
			float unscaledTime = Time.unscaledTime;
			CompleteSave(wait: false);
			if (!_session.IsDedicated())
			{
				if (Object.op_Implicit((Object)(object)Player.m_localPlayer) && !InputBusy())
				{
					KeyboardShortcut value = _toggle.Value;
					if (((KeyboardShortcut)(ref value)).IsDown())
					{
						_labels.Value = !_labels.Value;
					}
				}
				_bridge.Tick(_map, _mapBudget.Value, unscaledTime);
			}
			if (_session.IsServer())
			{
				_hostRange = _serverRange.Value;
				if (unscaledTime >= _nextRoster)
				{
					_nextRoster = unscaledTime + 0.2f;
					BroadcastRoster(unscaledTime);
				}
				if (unscaledTime >= _nextSave)
				{
					_nextSave = unscaledTime + (float)_saveInterval.Value;
					Save();
				}
			}
			else
			{
				if (unscaledTime >= _nextHello)
				{
					_nextHello = unscaledTime + (float)(_connected ? 15 : 2);
					Send(ServerId, new Packet
					{
						Kind = MessageKind.Hello
					}, _client);
				}
				if (_connected && Object.op_Implicit((Object)(object)Player.m_localPlayer) && unscaledTime >= _nextPosition)
				{
					_nextPosition = unscaledTime + 0.2f;
					Send(ServerId, new Packet
					{
						Kind = MessageKind.Position,
						Sequence = ++_positionSequence,
						Friends = new Presence[1] { LocalPresence() }
					}, _client);
				}
			}
			if (unscaledTime < _nextNetwork)
			{
				return;
			}
			_nextNetwork = unscaledTime + 0.1f;
			PumpPinEdits(unscaledTime);
			if (_session.IsServer())
			{
				foreach (KeyValuePair<long, PeerState> peer in _peers)
				{
					Packet packet = peer.Value.Channel.Next(_map, unscaledTime);
					if (packet != null)
					{
						Send(peer.Key, packet, peer.Value.Client);
					}
					Packet packet2 = peer.Value.Pins.Next(_pinBoard, unscaledTime);
					if (packet2 != null)
					{
						Send(peer.Key, packet2, peer.Value.Client);
					}
				}
				return;
			}
			if (_connected)
			{
				Packet packet3 = _upload.Next(_map, unscaledTime);
				if (packet3 != null)
				{
					Send(ServerId, packet3, _client);
				}
			}
		}

		private void LateUpdate()
		{
			if (Object.op_Implicit((Object)(object)_session) && !_session.IsDedicated())
			{
				_settings.Enabled = _connected;
				_settings.ShowMapPins = _pins.Value;
				_settings.ShowWorldLabels = _labels.Value;
				_settings.WorldRange = Mathf.Min(_range.Value, _hostRange);
				_settings.LabelScale = _scale.Value;
				_settings.Opacity = _opacity.Value;
				_settings.ReducedMotion = _reduced.Value;
				_settings.AdaptiveQuality = _adaptive.Value;
				_settings.ShowDistance = _distance.Value;
				_settings.ShowEdgeIndicators = _edges.Value;
				_settings.StaleSeconds = 5f;
				_settings.ShowPlayerLegend = _legendEnabled.Value;
				if (_hud == null)
				{
					_hud = new FriendHud();
				}
				_hud.Tick(_friends, _settings, Time.unscaledTime, Time.unscaledDeltaTime);
				TickSharedPresentation(Time.unscaledTime);
			}
		}

		private bool EnsureSession()
		{
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Expected O, but got Unknown
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Expected O, but got Unknown
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			ZNet instance = ZNet.instance;
			if (Object.op_Implicit((Object)(object)instance) && ((Behaviour)instance).enabled && Object.op_Implicit((Object)(object)Game.instance) && instance.GetWorld() != null)
			{
				if (!instance.IsServer())
				{
					ZNetPeer serverPeer = instance.GetServerPeer();
					if (serverPeer == null || !serverPeer.IsReady())
					{
						goto IL_0049;
					}
				}
				long worldUID = instance.GetWorldUID();
				if ((Object)(object)_session == (Object)(object)instance && _world == worldUID && _map != null)
				{
					return true;
				}
				EndSession();
				_session = instance;
				_world = worldUID;
				_client = NewNonce();
				_epoch = (instance.IsServer() ? NewNonce() : 0);
				_map = new ExplorationMap(2048, 2048);
				_bridge = (instance.IsDedicated() ? null : new MapBridge());
				_upload = (instance.IsServer() ? null : new ChunkChannel(64));
				_connected = instance.IsServer();
				_nextSave = Time.unscaledTime + (float)_saveInterval.Value;
				if (instance.IsServer())
				{
					_store = new WorldExplorationStore(Path.Combine(Paths.ConfigPath, "Ravenbond", "worlds"));
					try
					{
						ExplorationLoadResult val = _store.Load(worldUID, 2048, 2048);
						_map = val.Map;
						_savedPixels = _map.ExploredCount;
						if (!string.IsNullOrEmpty(val.Warning))
						{
							_storageStatus = val.Warning;
							((BaseUnityPlugin)this).Logger.LogWarning((object)val.Warning);
						}
						if ((int)val.Source == 0 && !string.IsNullOrEmpty(val.Warning))
						{
							_store = null;
							_storageStatus += " Autosave disabled for this session to preserve damaged files for recovery.";
							((BaseUnityPlugin)this).Logger.LogWarning((object)_storageStatus);
						}
					}
					catch (Exception ex)
					{
						_store = null;
						_storageStatus = "Shared map storage unavailable; existing files preserved.";
						((BaseUnityPlugin)this).Logger.LogError((object)(_storageStatus + " " + ex.Message));
					}
				}
				StartSharedPins();
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Waybound session started for world " + worldUID));
				return true;
			}
			goto IL_0049;
			IL_0049:
			if (_map != null)
			{
				EndSession();
			}
			return false;
		}

		private static long NewNonce()
		{
			return BitConverter.ToInt64(Guid.NewGuid().ToByteArray(), 0);
		}

		private static bool InputBusy()
		{
			if (!Console.IsVisible() && (!Object.op_Implicit((Object)(object)Chat.instance) || !Chat.instance.HasFocus()) && !TextInput.IsVisible() && !Menu.IsVisible() && !InventoryGui.IsVisible() && !Minimap.IsOpen() && !StoreGui.IsVisible())
			{
				return UnifiedPopup.IsVisible();
			}
			return true;
		}

		private Presence LocalPresence()
		{
			//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_004b: 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_0063: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			Vector3 position = ((Component)localPlayer).transform.position;
			return new Presence
			{
				Id = ZNet.GetUID(),
				OwnerId = LocalOwner,
				ColorIndex = _localColorIndex,
				Name = Wire.CleanName(localPlayer.GetPlayerName()),
				X = position.x,
				Y = position.y,
				Z = position.z,
				Dead = ((Character)localPlayer).IsDead()
			};
		}

		private void BroadcastRoster(float now)
		{
			_roster.Clear();
			_departed.Clear();
			if (!_session.IsDedicated() && Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				_roster.Add(LocalPresence());
			}
			foreach (KeyValuePair<long, PeerState> peer2 in _peers)
			{
				ZNetPeer peer = _session.GetPeer(peer2.Key);
				if (peer == null || !peer.IsReady() || peer != peer2.Value.Connection)
				{
					_departed.Add(peer2.Key);
					continue;
				}
				Presence presence = peer2.Value.Presence;
				if (presence != null && now - peer2.Value.Seen < 5f && _roster.Count < 64)
				{
					_roster.Add(presence);
				}
			}
			foreach (long item in _departed)
			{
				_peers.Remove(item);
			}
			Packet packet = new Packet
			{
				Kind = MessageKind.Roster,
				Sequence = ++_rosterSequence,
				Friends = _roster.ToArray()
			};
			SetFriends(packet.Friends, now);
			foreach (KeyValuePair<long, PeerState> peer3 in _peers)
			{
				Send(peer3.Key, packet, peer3.Value.Client);
			}
		}

		private void SetFriends(Presence[] roster, float now)
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			_friends.Clear();
			_lastRoster = now;
			_legendRoster.Clear();
			_legendRoster.AddRange(roster);
			_legendDirty = true;
			long uID = ZNet.GetUID();
			foreach (Presence presence in roster)
			{
				if (presence.Id == uID)
				{
					_localColorIndex = presence.ColorIndex;
				}
				else
				{
					_friends.Add(new FriendView(presence.Id, presence.Name, new Vector3(presence.X, presence.Y, presence.Z), presence.Dead, now, presence.ColorIndex));
				}
			}
		}

		private IEnumerator ReceiveServer(long sender, ZPackage package)
		{
			if (!EnsureSession() || !_session.IsServer())
			{
				yield break;
			}
			ZNetPeer peer = _session.GetPeer(sender);
			if (peer == null || !peer.IsReady())
			{
				yield break;
			}
			Packet packet = Decode(package);
			if (packet == null || packet.World != _world)
			{
				yield break;
			}
			float unscaledTime = Time.unscaledTime;
			if (packet.Kind == MessageKind.Hello)
			{
				_peers.TryGetValue(sender, out PeerState value);
				bool flag = value != null && value.Connection == peer;
				if (flag && unscaledTime - value.LastHello < 1f)
				{
					yield break;
				}
				if (!flag || value.Client != packet.Client)
				{
					if (_peers.Count >= 63 && value == null)
					{
						yield break;
					}
					long num = PeerIdentity.Resolve(peer, Game.instance, ZDOMan.instance);
					if (num == 0L)
					{
						yield break;
					}
					value = (_peers[sender] = new PeerState
					{
						Client = packet.Client,
						Owner = num,
						Connection = peer
					});
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Waybound connected: " + Wire.CleanName(peer.m_playerName)));
				}
				value.LastHello = unscaledTime;
				Send(sender, new Packet
				{
					Kind = MessageKind.Welcome,
					Range = _serverRange.Value,
					ColorIndex = _pinBoard.GetColorIndex(value.Owner)
				}, packet.Client);
			}
			else
			{
				if (packet.Epoch != _epoch || !_peers.TryGetValue(sender, out PeerState value2) || value2.Client != packet.Client || value2.Connection != peer)
				{
					yield break;
				}
				if (unscaledTime - value2.Window >= 1f)
				{
					value2.Window = unscaledTime;
					value2.Packets = 0;
				}
				if (++value2.Packets > 40)
				{
					yield break;
				}
				switch (packet.Kind)
				{
				case MessageKind.PinEdit:
					if (packet.Sequence > value2.PinOperation)
					{
						value2.PinResult = CommitPinEdit(packet, value2.Owner, peer.m_playerName);
						value2.PinOperation = packet.Sequence;
					}
					if (packet.Sequence == value2.PinOperation && value2.PinResult != null)
					{
						Send(sender, value2.PinResult, value2.Client);
					}
					break;
				case MessageKind.PinAck:
					value2.Pins.Acknowledge(packet.Sequence);
					break;
				case MessageKind.MapChunk:
					_map.MergeChunk(packet.Chunk, packet.Data);
					Send(sender, new Packet
					{
						Kind = MessageKind.MapAck,
						Chunk = packet.Chunk,
						Sequence = packet.Sequence
					}, value2.Client);
					break;
				case MessageKind.MapAck:
					value2.Channel.Acknowledge(packet.Chunk, packet.Sequence);
					break;
				case MessageKind.Position:
					if (packet.Sequence > value2.PositionSequence)
					{
						Presence presence = packet.Friends[0];
						presence.Id = sender;
						presence.Name = Wire.CleanName(peer.m_playerName);
						presence.OwnerId = value2.Owner;
						presence.ColorIndex = _pinBoard.GetColorIndex(presence.OwnerId);
						value2.Presence = presence;
						value2.Seen = unscaledTime;
						value2.PositionSequence = packet.Sequence;
					}
					break;
				}
			}
		}

		private IEnumerator ReceiveClient(long sender, ZPackage package)
		{
			if (!EnsureSession() || _session.IsServer() || sender != ServerId)
			{
				yield break;
			}
			Packet packet = Decode(package);
			if (packet == null || packet.World != _world || packet.Client != _client)
			{
				yield break;
			}
			if (packet.Kind == MessageKind.Welcome)
			{
				if (_epoch != packet.Epoch)
				{
					_upload = new ChunkChannel(64);
					_receivedRosterSequence = 0L;
					_friends.Clear();
					ResetClientPins();
				}
				_epoch = packet.Epoch;
				_hostRange = packet.Range;
				_localColorIndex = packet.ColorIndex;
				_connected = true;
				_legendDirty = true;
			}
			else
			{
				if (!_connected || packet.Epoch != _epoch)
				{
					yield break;
				}
				switch (packet.Kind)
				{
				case MessageKind.PinResult:
					ReceivePinResult(packet);
					break;
				case MessageKind.PinBatch:
					if (packet.Sequence > _receivedPinRevision)
					{
						SharedPin[] pins = packet.Pins;
						foreach (SharedPin pin in pins)
						{
							ReceivePin(pin);
						}
						_receivedPinRevision = packet.Sequence;
					}
					Send(ServerId, new Packet
					{
						Kind = MessageKind.PinAck,
						Sequence = packet.Sequence
					}, _client);
					break;
				case MessageKind.MapChunk:
					_map.MergeChunk(packet.Chunk, packet.Data);
					Send(sender, new Packet
					{
						Kind = MessageKind.MapAck,
						Chunk = packet.Chunk,
						Sequence = packet.Sequence
					}, _client);
					break;
				case MessageKind.MapAck:
					_upload.Acknowledge(packet.Chunk, packet.Sequence);
					break;
				case MessageKind.Roster:
					if (packet.Sequence > _receivedRosterSequence)
					{
						_receivedRosterSequence = packet.Sequence;
						SetFriends(packet.Friends, Time.unscaledTime);
					}
					break;
				}
			}
		}

		private Packet? Decode(ZPackage package)
		{
			if (package.Size() > 12288)
			{
				return null;
			}
			return Wire.Decode(package.GetArray());
		}

		private void Send(long target, Packet packet, long client)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			if (target != 0L && ZRoutedRpc.instance != null && Object.op_Implicit((Object)(object)_session))
			{
				packet.World = _world;
				packet.Epoch = _epoch;
				packet.Client = client;
				_rpc.SendPackage(target, new ZPackage(Wire.Encode(packet)));
			}
		}

		private void Save()
		{
			SavePins(wait: false);
			CompleteSave(wait: false);
			if (_saveTask != null || _store == null || _map == null || _savedPixels == _map.ExploredCount)
			{
				return;
			}
			byte[] bytes = _map.CopyBytes();
			WorldExplorationStore store = _store;
			long world = _world;
			_savingPixels = _map.ExploredCount;
			_saveTask = Task.Run(delegate
			{
				try
				{
					store.Save(world, ExplorationMap.FromBytes(2048, 2048, bytes));
					return (string)null;
				}
				catch (Exception ex)
				{
					return ex.Message;
				}
			});
		}

		private void CompleteSave(bool wait)
		{
			if (_saveTask == null || (!wait && !_saveTask.IsCompleted))
			{
				return;
			}
			string result = _saveTask.GetAwaiter().GetResult();
			_saveTask = null;
			if (result == null)
			{
				_savedPixels = _savingPixels;
				_storageStatus = "";
				return;
			}
			_storageStatus = "Map save failed; see BepInEx log.";
			if (Time.unscaledTime >= _nextWarning)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)(_storageStatus + " " + result));
				_nextWarning = Time.unscaledTime + 30f;
			}
		}

		private void EndSession()
		{
			EndSharedPins();
			CompleteSave(wait: true);
			Save();
			CompleteSave(wait: true);
			_bridge?.Flush();
			_hud?.Dispose();
			_hud = null;
			_bridge = null;
			_store = null;
			_map = null;
			_upload = null;
			_session = null;
			_friends.Clear();
			_peers.Clear();
			_connected = false;
			_savedPixels = 0;
			_storageStatus = "";
			_epoch = (_world = (_client = (_positionSequence = (_rosterSequence = (_receivedRosterSequence = 0L)))));
			_nextHello = (_nextPosition = (_nextNetwork = (_nextRoster = (_nextSave = (_lastRoster = (_nextWarning = 0f))))));
		}

		private void OnDestroy()
		{
			EndSession();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		private void OnApplicationQuit()
		{
			SavePins(wait: true);
			CompleteSave(wait: true);
			Save();
			CompleteSave(wait: true);
		}

		private void StartSharedPins()
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Expected O, but got Unknown
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			_pinBridge = (_session.IsDedicated() ? null : new SharedPinBridge(QueuePinEdit));
			if (_session.IsServer())
			{
				_pinBoard = new SharedPinBoard();
				_pinStore = new SharedPinStore(Path.Combine(Paths.ConfigPath, "Ravenbond", "worlds"));
				try
				{
					SharedPinLoadResult val = _pinStore.Load(_world);
					_pinBoard = val.Board;
					_savedPinRevision = _pinBoard.Revision;
					if (!string.IsNullOrEmpty(val.Warning))
					{
						((BaseUnityPlugin)this).Logger.LogWarning((object)val.Warning);
					}
					if ((int)val.Source == 0 && !string.IsNullOrEmpty(val.Warning))
					{
						_pinStore = null;
					}
				}
				catch (Exception ex)
				{
					_pinStore = null;
					((BaseUnityPlugin)this).Logger.LogError((object)("Shared pin storage unavailable; existing files preserved. " + ex.Message));
				}
				foreach (SharedPin record in _pinBoard.Records)
				{
					_knownPins[record.Id] = record.Clone();
				}
				if (!_session.IsDedicated())
				{
					_localColorIndex = _pinBoard.GetColorIndex(LocalOwner);
				}
			}
			_legendDirty = true;
		}

		private void QueuePinEdit(PinEdit edit)
		{
			if (!_connected)
			{
				return;
			}
			if (_pinEdits.Count >= 4096 && !_pinEdits.ContainsKey(edit.Id))
			{
				_pinBridge?.RestorePrivate(edit.Id);
				return;
			}
			if (_pinEdits.TryGetValue(edit.Id, out QueuedEdit value))
			{
				edit.ExpectedRevision = value.Edit.ExpectedRevision;
			}
			_pinEdits[edit.Id] = new QueuedEdit
			{
				Edit = edit,
				Generation = ++_pinGeneration
			};
		}

		private bool PinPending(long id)
		{
			if (!_pinEdits.ContainsKey(id))
			{
				Packet? pendingPin = _pendingPin;
				if (pendingPin == null)
				{
					return false;
				}
				return pendingPin.PinEdit?.Id == id;
			}
			return true;
		}

		private void PumpPinEdits(float now)
		{
			//IL_0066: 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_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: 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_009b: 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_00b3: 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_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Expected O, but got Unknown
			CompletePinSave(wait: false);
			if (!_connected)
			{
				return;
			}
			if (_pendingPin == null)
			{
				using Dictionary<long, QueuedEdit>.ValueCollection.Enumerator enumerator = _pinEdits.Values.GetEnumerator();
				if (enumerator.MoveNext())
				{
					QueuedEdit current = enumerator.Current;
					PinEdit edit = current.Edit;
					_pendingPin = new Packet
					{
						Kind = MessageKind.PinEdit,
						Sequence = ++_pinSequence,
						PinEdit = new PinEdit
						{
							Id = edit.Id,
							ExpectedRevision = edit.ExpectedRevision,
							Name = edit.Name,
							Type = edit.Type,
							X = edit.X,
							Y = edit.Y,
							Z = edit.Z,
							Checked = edit.Checked,
							Deleted = edit.Deleted
						}
					};
					_pendingGeneration = current.Generation;
					_nextPinRetry = 0f;
				}
			}
			if (_pendingPin != null && !(now < _nextPinRetry))
			{
				_nextPinRetry = now + 2f;
				if (_session.IsServer())
				{
					ReceivePinResult(CommitPinEdit(_pendingPin, LocalOwner, Object.op_Implicit((Object)(object)Player.m_localPlayer) ? Player.m_localPlayer.GetPlayerName() : "Host"));
				}
				else
				{
					Send(ServerId, _pendingPin, _client);
				}
			}
		}

		private Packet CommitPinEdit(Packet packet, long owner, string name)
		{
			SharedPin val = default(SharedPin);
			bool accepted = _pinBoard.TryApply(packet.PinEdit, owner, name, ref val);
			if (val != null)
			{
				ReceivePin(val.Clone());
			}
			Packet packet2 = new Packet();
			packet2.Kind = MessageKind.PinResult;
			packet2.Sequence = packet.Sequence;
			packet2.Accepted = accepted;
			packet2.Pins = (SharedPin[])((val == null) ? ((Array)Array.Empty<SharedPin>()) : ((Array)new SharedPin[1] { val.Clone() }));
			return packet2;
		}

		private void ReceivePinResult(Packet packet)
		{
			if (_pendingPin == null || packet.Sequence != _pendingPin.Sequence)
			{
				return;
			}
			PinEdit pinEdit = _pendingPin.PinEdit;
			long id = pinEdit.Id;
			QueuedEdit value;
			bool flag = _pinEdits.TryGetValue(id, out value) && value.Generation == _pendingGeneration;
			if (flag)
			{
				_pinEdits.Remove(id);
			}
			_pendingPin = null;
			if (packet.Pins.Length == 1)
			{
				SharedPin val = packet.Pins[0];
				bool flag2 = packet.Accepted || SameEdit(val, pinEdit);
				if (!flag && value != null)
				{
					if (flag2 && !val.Deleted)
					{
						value.Edit.ExpectedRevision = val.Revision;
					}
					else
					{
						_pinEdits.Remove(id);
					}
				}
				ReceivePin(packet.Pins[0]);
				if (_knownPins.TryGetValue(id, out SharedPin value2))
				{
					_pinBridge?.Apply(value2, PinPending(id));
				}
			}
			else
			{
				_pinEdits.Remove(id);
				_pinBridge?.RestorePrivate(id);
				if (!packet.Accepted)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)"A shared pin could not be accepted by the host; any surviving new marker was kept as a private pin.");
				}
			}
		}

		private static bool SameEdit(SharedPin pin, PinEdit edit)
		{
			if (pin.Id == edit.Id && pin.Deleted == edit.Deleted)
			{
				if (!pin.Deleted)
				{
					if (pin.Name == SharedPinRules.CleanName(edit.Name) && pin.Type == edit.Type && pin.X == edit.X && pin.Y == edit.Y && pin.Z == edit.Z)
					{
						return pin.Checked == edit.Checked;
					}
					return false;
				}
				return true;
			}
			return false;
		}

		private void ReceivePin(SharedPin pin)
		{
			if (!_knownPins.TryGetValue(pin.Id, out SharedPin value) || value.Revision < pin.Revision)
			{
				_knownPins[pin.Id] = pin.Clone();
				if (pin.Deleted)
				{
					_pinEdits.Remove(pin.Id);
				}
				_pinBridge?.Apply(pin, PinPending(pin.Id));
				_legendDirty = true;
			}
		}

		private void TickSharedPresentation(float now)
		{
			_pinBridge?.Tick(_knownPins, PinPending, now);
			if (_legendDirty)
			{
				_legendDirty = false;
				_legendEntries.Clear();
				_legendOwners.Clear();
				if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
				{
					_legendOwners.Add(LocalOwner);
					_legendEntries.Add(new LegendEntry(LocalOwner, Player.m_localPlayer.GetPlayerName(), _localColorIndex, online: true));
				}
				foreach (Presence item in _legendRoster)
				{
					if (_legendOwners.Add(item.OwnerId))
					{
						_legendEntries.Add(new LegendEntry(item.OwnerId, item.Name, item.ColorIndex, online: true));
					}
				}
				foreach (SharedPin value in _knownPins.Values)
				{
					if (!value.Deleted && _legendOwners.Add(value.OwnerId))
					{
						_legendEntries.Add(new LegendEntry(value.OwnerId, value.OwnerName, value.ColorIndex, online: false));
					}
				}
			}
			if (_legend == null)
			{
				_legend = new PlayerLegend();
			}
			_legend.Tick(_legendEntries, _connected && _settings.ShowPlayerLegend, _localColorIndex);
		}

		private void SavePins(bool wait)
		{
			CompletePinSave(wait);
			if (_pinSaveTask == null && _pinStore != null && _pinBoard != null && _pinBoard.Revision != _savedPinRevision)
			{
				SharedPinBoard snapshot = _pinBoard.Clone();
				SharedPinStore store = _pinStore;
				long world = _world;
				_savingPinRevision = snapshot.Revision;
				_pinSaveTask = Task.Run(delegate
				{
					try
					{
						store.Save(world, snapshot);
						return (string)null;
					}
					catch (Exception ex)
					{
						return ex.Message;
					}
				});
			}
			if (wait)
			{
				CompletePinSave(wait: true);
			}
		}

		private void CompletePinSave(bool wait)
		{
			if (_pinSaveTask != null && (wait || _pinSaveTask.IsCompleted))
			{
				string result = _pinSaveTask.GetAwaiter().GetResult();
				_pinSaveTask = null;
				if (result == null)
				{
					_savedPinRevision = _savingPinRevision;
				}
				else
				{
					((BaseUnityPlugin)this).Logger.LogError((object)("Shared pin save failed; previous snapshot preserved. " + result));
				}
			}
		}

		private void ResetClientPins()
		{
			foreach (long key in _pinEdits.Keys)
			{
				if (!_knownPins.ContainsKey(key))
				{
					_pinBridge?.RestorePrivate(key);
				}
			}
			_pinBridge?.Dispose();
			_knownPins.Clear();
			_pinEdits.Clear();
			_pendingPin = null;
			_receivedPinRevision = (_pinGeneration = (_pendingGeneration = (_pinSequence = 0L)));
			_nextPinRetry = 0f;
			_legendRoster.Clear();
			_legendDirty = true;
		}

		private void EndSharedPins()
		{
			SavePins(wait: true);
			ResetClientPins();
			_pinBridge = null;
			_pinBoard = null;
			_pinStore = null;
			_savedPinRevision = (_savingPinRevision = 0L);
			_legend?.Dispose();
			_legend = null;
			_legendEntries.Clear();
			_legendOwners.Clear();
			_localColorIndex = 0;
		}
	}
	internal sealed class SharedPinBridge : IDisposable
	{
		private sealed class Entry
		{
			internal PinData Pin;

			internal SharedPin Record;

			internal PinEdit Observed;

			internal bool Removed;
		}

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

		private readonly Dictionary<long, Entry> _entries = new Dictionary<long, Entry>();

		private readonly Dictionary<PinData, long> _ids = new Dictionary<PinData, long>();

		private readonly List<PinEdit> _observedChanges = new List<PinEdit>();

		private readonly Action<PinEdit> _changed;

		private Minimap? _map;

		private bool _applying;

		private float _nextObserve;

		internal SharedPinBridge(Action<PinEdit> changed)
		{
			_changed = changed;
		}

		internal void Capture(Minimap map, PinData pin, long ownerId, string ownerName, int color)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected I4, but got Unknown
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Expected O, but got Unknown
			if (!_applying && MapBridge.Ready(map) && pin.m_save && !_ids.ContainsKey(pin) && Supported((int)pin.m_type))
			{
				Bind(map);
				long num;
				do
				{
					num = BitConverter.ToInt64(Guid.NewGuid().ToByteArray(), 0) & 0x7FFFFFFFFFFFFFFFL;
				}
				while (num == 0L || _entries.ContainsKey(num));
				PinEdit val = Read(num, pin);
				Entry value = new Entry
				{
					Pin = pin,
					Observed = val,
					Record = new SharedPin
					{
						Id = num,
						OwnerId = ownerId,
						OwnerName = ownerName,
						ColorIndex = color
					}
				};
				_entries.Add(num, value);
				_ids.Add(pin, num);
				pin.m_ownerID = 0L;
				_changed(val);
			}
		}

		internal void Removed(Minimap map, PinData pin)
		{
			if (!_applying && !((Object)(object)map != (Object)(object)_map) && _ids.TryGetValue(pin, out var value))
			{
				Entry entry = _entries[value];
				if (!entry.Removed)
				{
					entry.Removed = true;
					PinEdit val = Read(value, pin);
					val.Deleted = true;
					entry.Observed = val;
					val.ExpectedRevision = entry.Record.Revision;
					_changed(val);
				}
			}
		}

		internal void Tick(IReadOnlyDictionary<long, SharedPin> records, Func<long, bool> pending, float now)
		{
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			Minimap instance = Minimap.instance;
			if (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || !MapBridge.Ready(instance))
			{
				return;
			}
			Bind(instance);
			if (now >= _nextObserve)
			{
				_nextObserve = now + 0.15f;
				_observedChanges.Clear();
				foreach (KeyValuePair<long, Entry> entry in _entries)
				{
					Entry value = entry.Value;
					if (!value.Removed)
					{
						PinEdit val = Read(entry.Key, value.Pin);
						val.ExpectedRevision = value.Record.Revision;
						if (!Same(val, value.Observed))
						{
							value.Observed = val;
							_observedChanges.Add(val);
						}
					}
				}
				foreach (PinEdit observedChange in _observedChanges)
				{
					_changed(observedChange);
				}
				foreach (SharedPin value2 in records.Values)
				{
					Apply(value2, pending(value2.Id));
				}
			}
			foreach (Entry value3 in _entries.Values)
			{
				if (value3.Removed)
				{
					continue;
				}
				PinData pin = value3.Pin;
				Color color = PlayerColors.Get(value3.Record.ColorIndex);
				if (Object.op_Implicit((Object)(object)pin.m_iconElement))
				{
					((Graphic)pin.m_iconElement).color = color;
				}
				PinNameData namePinData = pin.m_NamePinData;
				TMP_Text val2 = ((namePinData != null) ? namePinData.PinNameText : null);
				if (Object.op_Implicit((Object)(object)val2))
				{
					val2.richText = false;
					((Graphic)val2).color = color;
					if (val2.text != pin.m_name)
					{
						val2.text = pin.m_name;
					}
				}
			}
		}

		internal void Apply(SharedPin record, bool preserveLocal)
		{
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Invalid comparison between Unknown and I4
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_025a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0264: Expected O, but got Unknown
			if (!Object.op_Implicit((Object)(object)_map) || !MapBridge.Ready(_map))
			{
				return;
			}
			_applying = true;
			try
			{
				_entries.TryGetValue(record.Id, out Entry value);
				if (record.Deleted)
				{
					if (value != null)
					{
						_map.RemovePin(value.Pin);
						_ids.Remove(value.Pin);
						_entries.Remove(record.Id);
					}
					return;
				}
				if (value != null)
				{
					PinEdit val = Read(record.Id, value.Pin);
					if (!preserveLocal && !value.Removed && !Same(val, value.Observed))
					{
						val.ExpectedRevision = value.Record.Revision;
						value.Observed = val;
						_changed(val);
						preserveLocal = true;
					}
					value.Record = record;
					value.Pin.m_save = false;
					if (preserveLocal || (!value.Removed && Same(Read(record.Id, value.Pin), Edit(record))))
					{
						return;
					}
				}
				if (value != null && (int)value.Pin.m_type != record.Type)
				{
					_map.RemovePin(value.Pin);
					_ids.Remove(value.Pin);
					value.Removed = true;
				}
				if (value == null || value.Removed)
				{
					if (value != null)
					{
						_ids.Remove(value.Pin);
					}
					value = new Entry
					{
						Pin = _map.AddPin(new Vector3(record.X, record.Y, record.Z), (PinType)record.Type, record.Name, false, record.Checked, 0L, default(PlatformUserID)),
						Record = record
					};
					_entries[record.Id] = value;
					_ids[value.Pin] = record.Id;
				}
				PinData pin = value.Pin;
				pin.m_pos = new Vector3(record.X, record.Y, record.Z);
				pin.m_checked = record.Checked;
				pin.m_name = record.Name;
				pin.m_ownerID = 0L;
				pin.m_save = false;
				if (pin.m_NamePinData == null && !string.IsNullOrEmpty(pin.m_name))
				{
					pin.m_NamePinData = new PinNameData(pin);
				}
				PinNameData namePinData = pin.m_NamePinData;
				if (Object.op_Implicit((Object)(object)((namePinData != null) ? namePinData.PinNameText : null)))
				{
					pin.m_NamePinData.PinNameText.richText = false;
					pin.m_NamePinData.PinNameText.text = pin.m_name;
				}
				value.Observed = Read(record.Id, pin);
				value.Removed = false;
				NeedsUpdate.Invoke(_map) = true;
			}
			finally
			{
				_applying = false;
			}
		}

		internal void RestorePrivate(long id)
		{
			if (_entries.TryGetValue(id, out Entry value))
			{
				if (!value.Removed)
				{
					value.Pin.m_save = true;
				}
				_ids.Remove(value.Pin);
				_entries.Remove(id);
			}
		}

		internal void IncludeClosest(Minimap map, Vector3 position, float radius, bool mustBeVisible, ref PinData result)
		{
			//IL_0014: 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)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)map != (Object)(object)_map)
			{
				return;
			}
			float num = ((result == null) ? radius : DistanceXZ(position, result.m_pos));
			foreach (Entry value in _entries.Values)
			{
				PinData pin = value.Pin;
				if (!value.Removed && (!mustBeVisible || (Object.op_Implicit((Object)(object)pin.m_uiElement) && ((Component)pin.m_uiElement).gameObject.activeInHierarchy)))
				{
					float num2 = DistanceXZ(position, pin.m_pos);
					if (num2 < num)
					{
						num = num2;
						result = pin;
					}
				}
			}
		}

		private void Bind(Minimap map)
		{
			if (!((Object)(object)_map == (Object)(object)map))
			{
				Dispose();
				_map = map;
				_nextObserve = 0f;
			}
		}

		private static float DistanceXZ(Vector3 a, Vector3 b)
		{
			//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)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			float num = a.x - b.x;
			float num2 = a.z - b.z;
			return Mathf.Sqrt(num * num + num2 * num2);
		}

		private static bool Supported(int type)
		{
			if (type != 0 && type != 1 && type != 2 && type != 3 && type != 4 && type != 6 && type != 9 && type != 14 && type != 15)
			{
				return type == 16;
			}
			return true;
		}

		private static PinEdit Read(long id, PinData pin)
		{
			//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_000c: 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_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected I4, but got Unknown
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			return new PinEdit
			{
				Id = id,
				Name = SharedPinRules.CleanName(pin.m_name),
				Type = (int)pin.m_type,
				X = pin.m_pos.x,
				Y = pin.m_pos.y,
				Z = pin.m_pos.z,
				Checked = pin.m_checked
			};
		}

		private static PinEdit Edit(SharedPin pin)
		{
			//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_0011: 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_0029: 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_0041: 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_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Expected O, but got Unknown
			return new PinEdit
			{
				Id = pin.Id,
				Name = pin.Name,
				Type = pin.Type,
				X = pin.X,
				Y = pin.Y,
				Z = pin.Z,
				Checked = pin.Checked,
				Deleted = pin.Deleted
			};
		}

		private static bool Same(PinEdit a, PinEdit b)
		{
			if (a.Name == b.Name && a.Type == b.Type && a.X == b.X && a.Y == b.Y && a.Z == b.Z && a.Checked == b.Checked)
			{
				return a.Deleted == b.Deleted;
			}
			return false;
		}

		public void Dispose()
		{
			_applying = true;
			try
			{
				if (!Object.op_Implicit((Object)(object)_map))
				{
					return;
				}
				foreach (Entry value in _entries.Values)
				{
					_map.RemovePin(value.Pin);
				}
			}
			finally
			{
				_applying = false;
				_entries.Clear();
				_ids.Clear();
				_map = null;
			}
		}
	}
}
namespace Ravenbond.UI
{
	public sealed class FriendHud : IDisposable
	{
		private sealed class Marker
		{
			internal RectTransform Root;

			internal RectTransform IconRoot;

			internal RectTransform Plate;

			internal CanvasGroup Group;

			internal TextMeshProUGUI Name;

			internal TextMeshProUGUI Distance;

			internal MarkerGraphic Icon;

			internal FriendView Friend;

			internal Transform? LiveTransform;

			internal Vector3 WorldPosition;

			internal string DisplayName = "";

			internal int DistanceMetres = -1;

			internal int ColorIndex = -1;

			internal float CaptionOffset;

			internal bool Present;

			internal bool HasPosition;

			internal bool Dead;

			internal bool DistanceVisible;

			internal bool NeedsText;
		}

		internal static readonly Color Teal = new Color(0.48f, 0.88f, 0.78f, 1f);

		internal static readonly Color Fallen = new Color(0.85f, 0.57f, 0.49f, 1f);

		private static readonly Color Gold = new Color(0.91f, 0.77f, 0.48f, 1f);

		private const int MaximumLabels = 64;

		private readonly MapPins _mapPins = new MapPins();

		private readonly Dictionary<long, Marker> _active = new Dictionary<long, Marker>();

		private readonly Stack<Marker> _pool = new Stack<Marker>();

		private readonly List<long> _retired = new List<long>();

		private readonly Dictionary<long, Transform> _loadedPlayers = new Dictionary<long, Transform>();

		private readonly List<Rect> _captionBounds = new List<Rect>(64);

		private RectTransform? _root;

		private TMP_FontAsset? _font;

		private Material? _textMaterial;

		private GameCamera? _gameCamera;

		private Camera? _camera;

		private float _nextRoster;

		private float _nextText;

		private float _averageDelta = 1f / 60f;

		private bool _wasVisible;

		public void Tick(IReadOnlyList<FriendView> friends, HudSettings settings, float now, float dt)
		{
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			_mapPins.Tick(friends, settings, now, dt);
			Player localPlayer = Player.m_localPlayer;
			if (!settings.Enabled || !settings.ShowWorldLabels || !Object.op_Implicit((Object)(object)localPlayer) || !Object.op_Implicit((Object)(object)Hud.instance) || Hud.IsUserHidden() || ((Character)localPlayer).IsDead() || ((Character)localPlayer).InCutscene() || ((Character)localPlayer).IsTeleporting() || (settings.HideWhenUiOpen && UiBusy()))
			{
				if (Object.op_Implicit((Object)(object)_root))
				{
					((Component)_root).gameObject.SetActive(false);
				}
				_wasVisible = false;
			}
			else if (Object.op_Implicit((Object)(object)_root) || Build())
			{
				if ((Object)(object)_gameCamera != (Object)(object)GameCamera.instance || !Object.op_Implicit((Object)(object)_camera))
				{
					_gameCamera = GameCamera.instance;
					_camera = (Object.op_Implicit((Object)(object)_gameCamera) ? ((Component)_gameCamera).GetComponent<Camera>() : Camera.main);
				}
				if (!Object.op_Implicit((Object)(object)_camera))
				{
					((Component)_root).gameObject.SetActive(false);
					_wasVisible = false;
				}
				else
				{
					((Component)_root).gameObject.SetActive(true);
					TickMarkers(friends, settings, now, dt, ((Component)localPlayer).transform.position, _camera);
				}
			}
		}

		private void TickMarkers(IReadOnlyList<FriendView> friends, HudSettings settings, float now, float dt, Vector3 observer, Camera camera)
		{
			//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_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_020a: Unknown result type (might be due to invalid IL or missing references)
			//IL_020c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			//IL_023c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0240: Unknown result type (might be due to invalid IL or missing references)
			//IL_0245: Unknown result type (might be due to invalid IL or missing references)
			//IL_024a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0277: Unknown result type (might be due to invalid IL or missing references)
			//IL_0267: Unknown result type (might be due to invalid IL or missing references)
			//IL_026c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Unknown result type (might be due to invalid IL or missing references)
			//IL_028a: Unknown result type (might be due to invalid IL or missing references)
			//IL_029a: Unknown result type (might be due to invalid IL or missing references)
			//IL_029f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02be: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_031e: Unknown result type (might be due to invalid IL or missing references)
			//IL_033e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0359: Unknown result type (might be due to invalid IL or missing references)
			//IL_0367: Unknown result type (might be due to invalid IL or missing references)
			//IL_0373: Unknown result type (might be due to invalid IL or missing references)
			//IL_044c: Unknown result type (might be due to invalid IL or missing references)
			//IL_045a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0460: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_037e: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0389: Unknown result type (might be due to invalid IL or missing references)
			//IL_0488: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03db: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0404: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_041f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0412: Unknown result type (might be due to invalid IL or missing references)
			//IL_0430: Unknown result type (might be due to invalid IL or missing references)
			//IL_043e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0443: Unknown result type (might be due to invalid IL or missing references)
			//IL_055c: Unknown result type (might be due to invalid IL or missing references)
			//IL_055e: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a6: Unknown result type (might be due to invalid IL or missing references)
			dt = Mathf.Clamp(dt, 0f, 1f);
			_averageDelta = Mathf.Lerp(_averageDelta, Mathf.Min(dt, 0.1f), 1f - Mathf.Exp(-2f * dt));
			float num = ((!settings.AdaptiveQuality) ? 0.12f : ((_averageDelta > 0.042f) ? 0.4f : ((_averageDelta > 0.025f) ? 0.24f : 0.12f)));
			if (now >= _nextRoster || !_wasVisible)
			{
				Reconcile(friends);
				_nextRoster = now + Mathf.Max(0.25f, num);
			}
			else
			{
				for (int i = 0; i < friends.Count; i++)
				{
					if (_active.TryGetValue(friends[i].Id, out Marker value))
					{
						value.Friend = friends[i];
					}
				}
			}
			bool flag = now >= _nextText || !_wasVisible;
			if (flag)
			{
				_nextText = now + num;
			}
			Rect rect = _root.rect;
			Vector2 size = ((Rect)(ref rect)).size;
			float num2 = Mathf.Clamp(settings.LabelScale, 0.65f, 1.8f);
			float num3 = Mathf.Clamp(settings.WorldRange, 10f, 10000f);
			float num4 = 1f - Mathf.Exp((0f - Mathf.Clamp(settings.PositionSharpness, 1f, 40f)) * dt);
			float num5 = (settings.ReducedMotion ? 1f : (1f - Mathf.Exp(-12f * dt)));
			_retired.Clear();
			_captionBounds.Clear();
			Vector2 val4 = default(Vector2);
			foreach (KeyValuePair<long, Marker> item in _active)
			{
				Marker value2 = item.Value;
				FriendView friend = value2.Friend;
				Vector3 val = ((Object.op_Implicit((Object)(object)value2.LiveTransform) && !friend.Dead) ? value2.LiveTransform.position : friend.Position);
				if (!MapPins.Finite(val))
				{
					value2.Group.alpha = 0f;
					continue;
				}
				int num6;
				if (value2.HasPosition && _wasVisible)
				{
					Vector3 val2 = val - value2.WorldPosition;
					num6 = ((((Vector3)(ref val2)).sqrMagnitude > 1600f) ? 1 : 0);
				}
				else
				{
					num6 = 1;
				}
				bool flag2 = (byte)num6 != 0;
				value2.WorldPosition = (flag2 ? val : Vector3.Lerp(value2.WorldPosition, val, num4));
				value2.HasPosition = true;
				float num7 = Vector3.Distance(observer, value2.WorldPosition);
				Vector3 val3 = camera.WorldToViewportPoint(value2.WorldPosition + Vector3.up * (friend.Dead ? 0.65f : 2.2f));
				((Vector2)(ref val4))..ctor((val3.x - 0.5f) * size.x, (val3.y - 0.5f) * size.y);
				float num8 = Mathf.Max(15f, size.x * 0.5f - 122f * num2);
				float num9 = Mathf.Max(15f, size.y * 0.5f - 106f * num2);
				float num10 = Mathf.Max(15f, size.y * 0.5f - 104f * num2);
				bool flag3 = val3.z <= 0f || val4.x < 0f - num8 || val4.x > num8 || val4.y > num9 || val4.y < 0f - num10;
				float num11 = 0f;
				if (flag3)
				{
					if (val3.z <= 0f)
					{
						val4 = -val4;
					}
					if (((Vector2)(ref val4)).sqrMagnitude < 0.01f)
					{
						val4 = Vector2.down;
					}
					num11 = Mathf.Atan2(val4.y, val4.x) * 57.29578f - 90f;
					float num12 = Mathf.Max(Mathf.Abs(val4.x) / num8, (val4.y > 0f) ? (val4.y / num9) : ((0f - val4.y) / num10));
					val4 /= Mathf.Max(0.0001f, num12);
				}
				value2.Root.anchoredPosition = val4;
				((Transform)value2.Root).localScale = Vector3.one * num2;
				((Transform)value2.IconRoot).localRotation = Quaternion.Euler(0f, 0f, flag3 ? num11 : 0f);
				value2.Icon.SetState(flag3, friend.Dead);
				float num13 = Mathf.Max(0f, now - friend.LastSeen);
				float num14 = 1f - Mathf.Clamp01((num13 - Mathf.Max(2f, settings.StaleSeconds - 3f)) / 3f);
				float num15 = 1f - Mathf.Clamp01((num7 - num3 * 0.9f) / Mathf.Max(1f, num3 * 0.1f));
				float num16 = ((value2.Present && (!flag3 || settings.ShowEdgeIndicators) && num7 > 1.2f) ? (Mathf.Clamp01(settings.Opacity) * num15 * num14) : 0f);
				float num17 = ((num16 > 0.01f) ? PlaceCaption(val4, size, num2) : 0f);
				value2.CaptionOffset = Mathf.Lerp(value2.CaptionOffset, num17, settings.ReducedMotion ? 1f : num5);
				value2.Plate.anchoredPosition = new Vector2(0f, 31f + value2.CaptionOffset);
				value2.Group.alpha = Mathf.Lerp(value2.Group.alpha, num16, num5);
				bool flag4 = value2.Group.alpha > 0.005f;
				if (((Component)value2.Root).gameObject.activeSelf != flag4)
				{
					((Component)value2.Root).gameObject.SetActive(flag4);
				}
				if (flag || value2.NeedsText)
				{
					UpdateText(value2, num7, settings.ShowDistance);
					value2.NeedsText = false;
				}
				if (!value2.Present && value2.Group.alpha <= 0.005f)
				{
					_retired.Add(item.Key);
				}
			}
			for (int j = 0; j < _retired.Count; j++)
			{
				long key = _retired[j];
				Marker marker = _active[key];
				((Component)marker.Root).gameObject.SetActive(false);
				marker.HasPosition = false;
				marker.LiveTransform = null;
				_active.Remove(key);
				_pool.Push(marker);
			}
			_wasVisible = true;
		}

		private static bool UiBusy()
		{
			if (!Minimap.IsOpen() && !InventoryGui.IsVisible() && !Console.IsVisible() && !TextInput.IsVisible() && (!Object.op_Implicit((Object)(object)Menu.instance) || !Menu.IsVisible()))
			{
				if (Object.op_Implicit((Object)(object)Chat.instance))
				{
					return Chat.instance.HasFocus();
				}
				return false;
			}
			return true;
		}

		private void Reconcile(IReadOnlyList<FriendView> friends)
		{
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			_loadedPlayers.Clear();
			List<Player> allPlayers = Player.GetAllPlayers();
			for (int i = 0; i < allPlayers.Count; i++)
			{
				Player val = allPlayers[i];
				if (Object.op_Implicit((Object)(object)val) && !((Object)(object)val == (Object)(object)Player.m_localPlayer))
				{
					ZNetView component = ((Component)val).GetComponent<ZNetView>();
					ZDO val2 = (Object.op_Implicit((Object)(object)component) ? component.GetZDO() : null);
					if (val2 != null)
					{
						_loadedPlayers[val2.GetOwner()] = ((Component)val).transform;
					}
				}
			}
			foreach (Marker value2 in _active.Values)
			{
				value2.Present = false;
			}
			for (int j = 0; j < friends.Count; j++)
			{
				FriendView friend = friends[j];
				if (!MapPins.Finite(friend.Position))
				{
					continue;
				}
				if (!_active.TryGetValue(friend.Id, out Marker value))
				{
					if (_active.Count >= 64)
					{
						continue;
					}
					value = ((_pool.Count > 0) ? _pool.Pop() : CreateMarker());
					_active.Add(friend.Id, value);
					value.Group.alpha = 0f;
					value.HasPosition = false;
					value.DistanceMetres = -1;
					value.CaptionOffset = 0f;
					value.NeedsText = true;
				}
				value.Friend = friend;
				value.Present = true;
				_loadedPlayers.TryGetValue(friend.Id, out value.LiveTransform);
			}
		}

		private float PlaceCaption(Vector2 projected, Vector2 canvasSize, float scale)
		{
			//IL_0024: 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)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: 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_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			Rect val = default(Rect);
			for (int i = 0; i < 9; i++)
			{
				float num = ((i != 0) ? ((i + 1) / 2 * 48 * ((i % 2 == 1) ? 1 : (-1))) : 0);
				((Rect)(ref val))..ctor(projected.x - 104f * scale, projected.y + (9f + num) * scale, 208f * scale, 44f * scale);
				if (((Rect)(ref val)).yMin < (0f - canvasSize.y) * 0.5f + 12f || ((Rect)(ref val)).yMax > canvasSize.y * 0.5f - 12f)
				{
					continue;
				}
				bool flag = false;
				for (int j = 0; j < _captionBounds.Count; j++)
				{
					Rect val2 = _captionBounds[j];
					if (((Rect)(ref val2)).Overlaps(val))
					{
						flag = true;
						break;
					}
				}
				if (!flag)
				{
					_captionBounds.Add(val);
					return num;
				}
			}
			return 0f;
		}

		private static void UpdateText(Marker marker, float distance, bool showDistance)
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			FriendView friend = marker.Friend;
			if (marker.DisplayName != friend.Name)
			{
				marker.DisplayName = friend.Name;
				((TMP_Text)marker.Name).text = friend.Name;
			}
			if (marker.ColorIndex != friend.ColorIndex || marker.Dead != friend.Dead)
			{
				marker.Dead = friend.Dead;
				marker.ColorIndex = friend.ColorIndex;
				Color color = PlayerColors.Get(friend.ColorIndex);
				((Graphic)marker.Name).color = color;
				((Graphic)marker.Icon).color = color;
			}
			if (marker.DistanceVisible != showDistance)
			{
				marker.DistanceVisible = showDistance;
				((Component)marker.Distance).gameObject.SetActive(showDistance);
			}
			int num = Mathf.RoundToInt(distance);
			if (showDistance && num != marker.DistanceMetres)
			{
				marker.DistanceMetres = num;
				((TMP_Text)marker.Distance).text = num.ToString(CultureInfo.InvariantCulture) + " m";
			}
		}

		private bool Build(Transform? parent = null, TMP_FontAsset? font = null)
		{
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Expected O, but got Unknown
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: 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)
			Transform val = parent;
			if (!Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)Hud.instance))
			{
				val = ((Component)Hud.instance).transform;
			}
			if (!Object.op_Implicit((Object)(object)val))
			{
				return false;
			}
			_active.Clear();
			_pool.Clear();
			_loadedPlayers.Clear();
			if (Object.op_Implicit((Object)(object)_textMaterial))
			{
				Object.Destroy((Object)(object)_textMaterial);
			}
			_font = font;
			if (!Object.op_Implicit((Object)(object)_font) && Object.op_Implicit((Object)(object)Hud.instance))
			{
				TMP_Text[] componentsInChildren = ((Component)Hud.instance).GetComponentsInChildren<TMP_Text>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					if (Object.op_Implicit((Object)(object)componentsInChildren[i].font))
					{
						_font = componentsInChildren[i].font;
						if (((Object)_font).name.IndexOf("Averia", StringComparison.OrdinalIgnoreCase) >= 0)
						{
							break;
						}
					}
				}
			}
			if (!Object.op_Implicit((Object)(object)_font))
			{
				_font = TMP_Settings.defaultFontAsset;
			}
			if (!Object.op_Implicit((Object)(object)_font) || !Object.op_Implicit((Object)(object)((TMP_Asset)_font).material))
			{
				return false;
			}
			_textMaterial = new Material(((TMP_Asset)_font).material)
			{
				name = "Ravenbond shared outlined labels"
			};
			if (_textMaterial.HasProperty(ShaderUtilities.ID_OutlineColor))
			{
				_textMaterial.SetColor(ShaderUtilities.ID_OutlineColor, new Color(0.02f, 0.035f, 0.037f, 1f));
			}
			if (_textMaterial.HasProperty(ShaderUtilities.ID_OutlineWidth))
			{
				_textMaterial.SetFloat(ShaderUtilities.ID_OutlineWidth, 0.17f);
			}
			_root = Rect("RavenbondFriendHud", val, Vector2.zero, Vector2.zero);
			Canvas obj = ((Component)_root).gameObject.AddComponent<Canvas>();
			obj.renderMode = (RenderMode)0;
			obj.sortingOrder = 25;
			obj.pixelPerfect = false;
			CanvasScaler obj2 = ((Component)_root).gameObject.AddComponent<CanvasScaler>();
			obj2.uiScaleMode = (ScaleMode)1;
			obj2.referenceResolution = new Vector2(1920f, 1080f);
			obj2.matchWidthOrHeight = 0.5f;
			CanvasGroup obj3 = ((Component)_root).gameObject.AddComponent<CanvasGroup>();
			obj3.interactable = false;
			obj3.blocksRaycasts = false;
			_wasVisible = false;
			_nextRoster = (_nextText = 0f);
			return true;
		}

		private Marker CreateMarker()
		{
			//IL_0011: 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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: 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_00ce: 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_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: 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_0170: Unknown result type (might be due to invalid IL or missing references)
			Marker marker = new Marker
			{
				Root = Rect("Friend", (Transform)(object)_root, Vector2.zero, new Vector2(220f, 74f))
			};
			marker.Group = ((Component)marker.Root).gameObject.AddComponent<CanvasGroup>();
			marker.Group.interactable = false;
			marker.Group.blocksRaycasts = false;
			RectTransform parent = (marker.Plate = Rect("Caption", (Transform)(object)marker.Root, new Vector2(0f, 31f), new Vector2(204f, 42f)));
			marker.Name = Label("Name", (Transform)(object)parent, new Vector2(0f, 7f), new Vector2(192f, 23f), 17f, new Color(0.95f, 0.96f, 0.9f));
			marker.Distance = Label("Distance", (Transform)(object)parent, new Vector2(0f, -12f), new Vector2(186f, 18f), 12f, Gold);
			marker.DistanceVisible = true;
			marker.IconRoot = Rect("FriendMarker", (Transform)(object)marker.Root, Vector2.zero, new Vector2(20f, 20f));
			marker.Icon = ((Component)marker.IconRoot).gameObject.AddComponent<MarkerGraphic>();
			((Graphic)marker.Icon).raycastTarget = false;
			((Graphic)marker.Icon).color = Teal;
			((Component)marker.Root).gameObject.SetActive(false);
			return marker;
		}

		private TextMeshProUGUI Label(string name, Transform parent, Vector2 position, Vector2 size, float fontSize, Color color)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = Rect(name, parent, position, size);
			((Component)val).gameObject.SetActive(false);
			TextMeshProUGUI obj = ((Component)val).gameObject.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj).font = _font;
			((TMP_Text)obj).fontSharedMaterial = _textMaterial;
			((TMP_Text)obj).fontSize = fontSize;
			((Graphic)obj).color = color;
			((Graphic)obj).raycastTarget = false;
			((TMP_Text)obj).richText = false;
			((TMP_Text)obj).textWrappingMode = (TextWrappingModes)0;
			((TMP_Text)obj).overflowMode = (TextOverflowModes)1;
			((TMP_Text)obj).alignment = (TextAlignmentOptions)514;
			((Component)val).gameObject.SetActive(true);
			return obj;
		}

		private static RectTransform Rect(string name, Transform parent, Vector2 position, Vector2 size)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: 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_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			RectTransform component = new GameObject(name, new Type[1] { typeof(RectTransform) }).GetComponent<RectTransform>();
			((Component)component).gameObject.layer = 5;
			((Transform)component).SetParent(parent, false);
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0.5f, 0.5f);
			component.pivot = val;
			Vector2 anchorMin = (component.anchorMax = val);
			component.anchorMin = anchorMin;
			component.anchoredPosition = position;
			component.sizeDelta = size;
			return component;
		}

		public void Dispose()
		{
			_mapPins.Dispose();
			if (Object.op_Implicit((Object)(object)_root))
			{
				Object.Destroy((Object)(object)((Component)_root).gameObject);
			}
			if (Object.op_Implicit((Object)(object)_textMaterial))
			{
				Object.Destroy((Object)(object)_textMaterial);
			}
			_root = null;
			_textMaterial = null;
			_font = null;
			_camera = null;
			_gameCamera = null;
			_active.Clear();
			_pool.Clear();
			_loadedPlayers.Clear();
			_retired.Clear();
			_wasVisible = false;
		}
	}
	public readonly struct FriendView
	{
		public readonly long Id;

		public readonly string Name;

		public readonly Vector3 Position;

		public readonly bool Dead;

		public readonly float LastSeen;

		public readonly int ColorIndex;

		public FriendView(long id, string name, Vector3 position, bool dead, float lastSeen, int colorIndex = 0)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			Id = id;
			Name = name ?? string.Empty;
			Position = position;
			Dead = dead;
			LastSeen = lastSeen;
			ColorIndex = colorIndex;
		}
	}
	public sealed class HudSettings
	{
		public bool Enabled = true;

		public bool ShowMapPins = true;

		public bool ShowPlayerLegend = true;

		public bool ShowWorldLabels;

		public bool ShowEdgeIndicators = true;

		public bool ShowDistance = true;

		public bool ReducedMotion;

		public bool AdaptiveQuality = true;

		public bool HideWhenUiOpen = true;

		public float WorldRange = 200f;

		public float LabelScale = 1f;

		public float PositionSharpness = 14f;

		public float Opacity = 0.95f;

		public float StaleSeconds = 12f;
	}
	internal sealed class MapPins : IDisposable
	{
		private sealed class Entry
		{
			internal PinData Pin;

			internal bool Present;

			internal string Name = "";

			internal bool Dead;
		}

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

		private readonly Dictionary<long, Entry> _pins = new Dictionary<long, Entry>();

		private readonly List<long> _removed = new List<long>();

		private Minimap? _map;

		internal void Tick(IReadOnlyList<FriendView> friends, HudSettings settings, float now, float dt)
		{
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: 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_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: 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_0149: 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_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Expected O, but got Unknown
			//IL_0287: Unknown result type (might be due to invalid IL or missing references)
			//IL_028c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_032f: Unknown result type (might be due to invalid IL or missing references)
			Minimap instance = Minimap.instance;
			if ((Object)(object)_map != (Object)(object)instance)
			{
				Clear();
				_map = instance;
			}
			if (!Object.op_Implicit((Object)(object)_map) || !settings.Enabled || !settings.ShowMapPins || !Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				Clear();
				return;
			}
			foreach (Entry value2 in _pins.Values)
			{
				value2.Present = false;
			}
			bool flag = false;
			float num = (settings.ReducedMotion ? 1f : (1f - Mathf.Exp((0f - Mathf.Clamp(settings.PositionSharpness, 1f, 40f)) * Mathf.Max(0f, dt))));
			for (int i = 0; i < friends.Count; i++)
			{
				FriendView friendView = friends[i];
				if (!Finite(friendView.Position) || now - friendView.LastSeen > Mathf.Max(2f, settings.StaleSeconds))
				{
					continue;
				}
				if (!_pins.TryGetValue(friendView.Id, out Entry value))
				{
					value = new Entry
					{
						Pin = _map.AddPin(friendView.Position, (PinType)10, friendView.Name, false, false, 0L, default(PlatformUserID))
					};
					_pins.Add(friendView.Id, value);
				}
				value.Present = true;
				PinData pin = value.Pin;
				Vector3 val = friendView.Position - pin.m_pos;
				Vector3 val2 = ((((Vector3)(ref val)).sqrMagnitude > 1600f) ? friendView.Position : Vector3.Lerp(pin.m_pos, friendView.Position, num));
				val = val2 - pin.m_pos;
				if (((Vector3)(ref val)).sqrMagnitude > 1E-06f)
				{
					pin.m_pos = val2;
					flag = true;
				}
				if (value.Name != friendView.Name || value.Dead != friendView.Dead)
				{
					value.Name = friendView.Name;
					value.Dead = friendView.Dead;
					pin.m_name = (friendView.Dead ? ("† " + friendView.Name) : friendView.Name);
					if (pin.m_NamePinData == null)
					{
						pin.m_NamePinData = new PinNameData(pin);
					}
					flag = true;
				}
				PinNameData namePinData = pin.m_NamePinData;
				TMP_Text val3 = ((namePinData != null) ? namePinData.PinNameText : null);
				Color color = PlayerColors.Get(friendView.ColorIndex);
				float num2 = Mathf.Max(0f, now - friendView.LastSeen);
				color.a = Mathf.Lerp(1f, 0.38f, Mathf.Clamp01((num2 - 3f) / Mathf.Max(1f, settings.StaleSeconds - 3f)));
				if (Object.op_Implicit((Object)(object)pin.m_iconElement))
				{
					((Graphic)pin.m_iconElement).color = color;
				}
				if (Object.op_Implicit((Object)(object)val3))
				{
					val3.richText = false;
					if (val3.text != pin.m_name)
					{
						val3.text = pin.m_name;
					}
					((Graphic)val3).color = color;
				}
			}
			_removed.Clear();
			foreach (KeyValuePair<long, Entry> pin2 in _pins)
			{
				if (!pin2.Value.Present)
				{
					_removed.Add(pin2.Key);
				}
			}
			for (int j = 0; j < _removed.Count; j++)
			{
				long key = _removed[j];
				_map.RemovePin(_pins[key].Pin);
				_pins.Remove(key);
			}
			if (flag)
			{
				NeedsUpdate.Invoke(_map) = true;
			}
		}

		internal static bool Finite(Vector3 value)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			if (!float.IsNaN(value.x) && !float.IsInfinity(value.x) && !float.IsNaN(value.y) && !float.IsInfinity(value.y) && !float.IsNaN(value.z))
			{
				return !float.IsInfinity(value.z);
			}
			return false;
		}

		private void Clear()
		{
			if (Object.op_Implicit((Object)(object)_map))
			{
				foreach (Entry value in _pins.Values)
				{
					_map.RemovePin(value.Pin);
				}
			}
			_pins.Clear();
			_removed.Clear();
		}

		public void Dispose()
		{
			Clear();
			_map = null;
		}
	}
	internal sealed class MarkerGraphic : MaskableGraphic
	{
		private bool _edge;

		private bool _dead;

		internal void SetState(bool edge, bool dead)
		{
			if (_edge != edge || _dead != dead)
			{
				_edge = edge;
				_dead = dead;
				((Graphic)this).SetVerticesDirty();
			}
		}

		protected override void OnPopulateMesh(VertexHelper vh)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: 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_0052: 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_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: 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_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: 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_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_020e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			vh.Clear();
			if (_edge)
			{
				Face(vh, new Vector2(0f, 8f), new Vector2(-6f, -5f), new Vector2(0f, -2f), ((Graphic)this).color);
				Face(vh, new Vector2(0f, 8f), new Vector2(0f, -2f), new Vector2(6f, -5f), ((Graphic)this).color);
				return;
			}
			if (_dead)
			{
				Quad(vh, new Vector2(-1.3f, -7f), new Vector2(1.3f, 7f), ((Graphic)this).color);
				Quad(vh, new Vector2(-5f, 1f), new Vector2(5f, 3.3f), ((Graphic)this).color);
				return;
			}
			Edge(vh, new Vector2(0f, 7f), new Vector2(6f, 0f), 1.6f, ((Graphic)this).color);
			Edge(vh, new Vector2(6f, 0f), new Vector2(0f, -7f), 1.6f, ((Graphic)this).color);
			Edge(vh, new Vector2(0f, -7f), new Vector2(-6f, 0f), 1.6f, ((Graphic)this).color);
			Edge(vh, new Vector2(-6f, 0f), new Vector2(0f, 7f), 1.6f, ((Graphic)this).color);
			Color color = ((Graphic)this).color;
			color.a *= 0.32f;
			Face(vh, new Vector2(0f, 3f), new Vector2(-2.5f, 0f), new Vector2(0f, -3f), color);
			Face(vh, new Vector2(0f, 3f), new Vector2(0f, -3f), new Vector2(2.5f, 0f), color);
		}

		private static void Face(VertexHelper vh, Vector2 a, Vector2 b, Vector2 c, Color tint)
		{
			//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_0010: 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_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: 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_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: 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)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: 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_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			int currentVertCount = vh.currentVertCount;
			vh.AddVert(Vector2.op_Implicit(a), Color32.op_Implicit(tint), Vector4.op_Implicit(Vector2.zero));
			vh.AddVert(Vector2.op_Implicit(b), Color32.op_Implicit(tint), Vector4.op_Implicit(Vector2.zero));
			vh.AddVert(Vector2.op_Implicit(c), Color32.op_Implicit(tint), Vector4.op_Implicit(Vector2.zero));
			vh.AddTriangle(currentVertCount, currentVertCount + 1, currentVertCount + 2);
		}

		private static void Quad(VertexHelper vh, Vector2 min, Vector2 max, Color tint)
		{
			//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_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: 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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: 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_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: 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_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: 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_008d: Unknown result type (might be due to invalid IL or missing references)
			int currentVertCount = vh.currentVertCount;
			vh.AddVert(Vector2.op_Implicit(min), Color32.op_Implicit(tint), Vector4.op_Implicit(Vector2.zero));
			vh.AddVert(Vector2.op_Implicit(new Vector2(min.x, max.y)), Color32.op_Implicit(tint), Vector4.op_Implicit(Vector2.zero));
			vh.AddVert(Vector2.op_Implicit(max), Color32.op_Implicit(tint), Vector4.op_Implicit(Vector2.zero));
			vh.AddVert(Vector2.op_Implicit(new Vector2(max.x, min.y)), Color32.op_Implicit(tint), Vector4.op_Implicit(Vector2.zero));
			vh.AddTriangle(currentVertCount, currentVertCount + 1, currentVertCount + 2);
			vh.AddTriangle(currentVertCount, currentVertCount + 2, currentVertCount + 3);
		}

		private static void Edge(VertexHelper vh, Vector2 a, Vector2 b, float width, Color tint)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//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_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: 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)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: 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_0042: 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