Decompiled source of SatelliteMap v0.2.0

SatelliteMap.dll

Decompiled 19 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using SatelliteMap.Render;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("SatelliteMap")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.0.0")]
[assembly: AssemblyInformationalVersion("0.2.0+c6634ddb2b62585c7dbb7e33e0955f3e3da99683")]
[assembly: AssemblyProduct("SatelliteMap")]
[assembly: AssemblyTitle("SatelliteMap")]
[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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SatelliteMap
{
	public static class SatelliteMapApi
	{
		private static readonly List<Func<int>> limits = new List<Func<int>>();

		private static Func<int, int, int> revisions;

		public static void SetZoneRevisionProvider(Func<int, int, int> provider)
		{
			revisions = provider;
		}

		public static bool Survey(int[] zoneXs, int[] zoneYs, Action<int[], int[], int[]> done)
		{
			return ZoneFeed.Survey(zoneXs, zoneYs, done);
		}

		internal static int RevisionOf(int zoneX, int zoneY)
		{
			Func<int, int, int> func = revisions;
			if (func == null)
			{
				return 0;
			}
			try
			{
				return Math.Max(0, func(zoneX, zoneY));
			}
			catch (Exception)
			{
				return 0;
			}
		}

		public static void AddDetailLimit(Func<int> provider)
		{
			if (provider != null)
			{
				limits.Add(provider);
			}
		}

		public static void NotifyExploredChanged()
		{
			SatMap.OnExploredChanged();
		}

		internal static int Limit(int level)
		{
			for (int i = 0; i < limits.Count; i++)
			{
				int num;
				try
				{
					num = limits[i]();
				}
				catch (Exception)
				{
					continue;
				}
				if (num >= 0 && num < level)
				{
					level = num;
				}
			}
			return level;
		}
	}
	internal struct Explored
	{
		private static readonly FieldRef<Minimap, BitArray> OwnRef = AccessTools.FieldRefAccess<Minimap, BitArray>("m_explored");

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

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

		private BitArray own;

		private BitArray others;

		private int size;

		internal bool SharedShown { get; private set; }

		internal static Explored Of(Minimap minimap)
		{
			Explored result = default(Explored);
			result.own = OwnRef.Invoke(minimap);
			result.others = OthersRef.Invoke(minimap);
			result.size = minimap.m_textureSize;
			result.SharedShown = ShowSharedRef.Invoke(minimap) && result.others != null;
			return result;
		}

		internal bool At(int x, int y)
		{
			int num = y * size + x;
			if (own == null || num < 0 || num >= own.Length)
			{
				return false;
			}
			if (!own[num])
			{
				if (SharedShown && num < others.Length)
				{
					return others[num];
				}
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Minimap), "Awake")]
	internal static class Minimap_Awake_Patch
	{
		private static void Postfix()
		{
			SatMap.Reset();
		}
	}
	[HarmonyPatch(typeof(ZNet), "OnDestroy")]
	internal static class ZNet_OnDestroy_Patch
	{
		private static void Postfix()
		{
			Snapshots.Close();
		}
	}
	[HarmonyPatch(typeof(Minimap), "Update")]
	internal static class Minimap_Update_Patch
	{
		private static void Postfix(Minimap __instance)
		{
			SatMap.Tick(__instance);
		}
	}
	[HarmonyPatch(typeof(Minimap), "Explore", new Type[]
	{
		typeof(int),
		typeof(int)
	})]
	internal static class Minimap_ExploreCell_Patch
	{
		private static void Postfix(bool __result)
		{
			if (__result)
			{
				SatMap.OnExploredChanged();
			}
		}
	}
	[HarmonyPatch(typeof(Minimap), "ExploreOthers", new Type[]
	{
		typeof(int),
		typeof(int)
	})]
	internal static class Minimap_ExploreOthers_Patch
	{
		private static void Postfix(bool __result)
		{
			if (__result)
			{
				SatMap.OnExploredChanged();
			}
		}
	}
	[HarmonyPatch(typeof(Minimap), "ResetAndExplore", new Type[]
	{
		typeof(BitArray),
		typeof(BitArray)
	})]
	internal static class Minimap_ResetAndExplore_Patch
	{
		private static void Postfix()
		{
			SatMap.OnExploredChanged();
		}
	}
	[HarmonyPatch(typeof(Minimap), "Reset")]
	internal static class Minimap_Reset_Patch
	{
		private static void Postfix()
		{
			SatMap.OnExploredChanged();
		}
	}
	[HarmonyPatch(typeof(Minimap), "ResetSharedMapData")]
	internal static class Minimap_ResetSharedMapData_Patch
	{
		private static void Postfix()
		{
			SatMap.OnExploredChanged();
		}
	}
	[BepInPlugin("qua8ion.valheim.satellitemap", "SatelliteMap", "0.2.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class SatelliteMapPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "qua8ion.valheim.satellitemap";

		public const string PluginName = "SatelliteMap";

		public const string PluginVersion = "0.2.0";

		internal static ManualLogSource Log;

		internal static SatelliteMapPlugin Instance;

		internal static ConfigEntry<int> DetailLevel;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> DebugTiles;

		internal static ConfigEntry<string> ServerSelfTest;

		private float selfTestAt = -1f;

		private bool selfTestDone;

		private void Awake()
		{
			//IL_0036: 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_0043: Expected O, but got Unknown
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			Instance = this;
			DetailLevel = ((BaseUnityPlugin)this).Config.Bind<int>("Rules", "DetailLevel", 3, new ConfigDescription("How detailed the picture of the explored part of the map is when you zoom in. 0: the plain game map only. 1: a sketch, 2 metres per pixel: relief, water, paths, fields, buildings, ruins and boss altars. 2: a good map, 1 metre per pixel: also the forest and large rocks. 3: the best map, half a metre per pixel: everything down to bushes. On a dedicated server the server's value applies to everyone.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 3), new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Draw the detailed picture on the big map. Your own switch, not synced.");
			DebugTiles = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DumpMapTiles", false, "Save every drawn piece of the map as PNG into BepInEx/cache/SatelliteMap/tiles and log the timings.");
			ServerSelfTest = ((BaseUnityPlugin)this).Config.Bind<string>("Debug", "ServerSelfTest", "", "For checking the server part without a client: \"x,z,radius\" in metres. Shortly after the world is loaded the server describes the zones around that point, writes the packet to BepInEx/cache/SatelliteMap/selftest.bin and runs the same zones through the request path a client would use. Empty: off.");
			ZoneFeed.RegisterRpc();
			new Harmony("qua8ion.valheim.satellitemap").PatchAll(Assembly.GetExecutingAssembly());
		}

		private void Update()
		{
			if (selfTestDone || string.IsNullOrEmpty(ServerSelfTest.Value))
			{
				return;
			}
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null || (Object)(object)ZoneSystem.instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null)
			{
				selfTestAt = -1f;
				return;
			}
			if (selfTestAt < 0f)
			{
				selfTestAt = Time.time + 15f;
			}
			if (Time.time >= selfTestAt)
			{
				selfTestDone = true;
				((MonoBehaviour)this).StartCoroutine(ZoneFeed.SelfTest(ServerSelfTest.Value));
			}
		}
	}
	internal static class SatGame
	{
		private static ShapeLibrary shapes;

		private const float HelperReach = 20f;

		internal static ShapeLibrary Shapes
		{
			get
			{
				if (shapes == null)
				{
					using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("shapes.bin"))
					{
						shapes = ShapeLibrary.Load(stream);
					}
					SatelliteMapPlugin.Log.LogInfo((object)("Top-down shapes of game objects: " + shapes.Count));
				}
				return shapes;
			}
		}

		internal static int DetailLevel()
		{
			if (!SatelliteMapPlugin.Enabled.Value)
			{
				return 0;
			}
			return SatelliteMapApi.Limit(Mathf.Clamp(SatelliteMapPlugin.DetailLevel.Value, 0, 3));
		}

		internal static bool IsForeignPiece(int prefabHash)
		{
			if (Shapes.Find(prefabHash, location: false) != null || Shapes.IsGamePrefab(prefabHash) || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return false;
			}
			GameObject prefab = ZNetScene.instance.GetPrefab(prefabHash);
			if ((Object)(object)prefab != (Object)null && (Object)(object)prefab.GetComponent<Piece>() != (Object)null && (Object)(object)prefab.GetComponent<WearNTear>() != (Object)null)
			{
				return (Object)(object)prefab.GetComponent<Rigidbody>() == (Object)null;
			}
			return false;
		}

		internal static bool TryRegisterForeign(int prefabHash)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: 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_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: 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_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: 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_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0253: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: 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_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_026b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Expected I4, but got Unknown
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: 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_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!IsForeignPiece(prefabHash))
				{
					return false;
				}
				GameObject prefab = ZNetScene.instance.GetPrefab(prefabHash);
				bool flag = false;
				Vector3 val = Vector3.zero;
				Vector3 val2 = Vector3.zero;
				Transform transform = prefab.transform;
				MeshFilter[] componentsInChildren = prefab.GetComponentsInChildren<MeshFilter>(false);
				foreach (MeshFilter val3 in componentsInChildren)
				{
					MeshRenderer component = ((Component)val3).GetComponent<MeshRenderer>();
					if ((Object)(object)val3.sharedMesh == (Object)null || (Object)(object)component == (Object)null || !((Renderer)component).enabled)
					{
						continue;
					}
					Bounds bounds = val3.sharedMesh.bounds;
					Vector3 val4 = transform.InverseTransformPoint(((Component)val3).transform.TransformPoint(((Bounds)(ref bounds)).center));
					if (val4.y < -20f || val4.y > 20f)
					{
						continue;
					}
					for (int j = 0; j < 8; j++)
					{
						Vector3 val5 = ((Bounds)(ref bounds)).center + Vector3.Scale(((Bounds)(ref bounds)).extents, new Vector3(((j & 1) == 0) ? (-1f) : 1f, ((j & 2) == 0) ? (-1f) : 1f, ((j & 4) == 0) ? (-1f) : 1f));
						Vector3 val6 = transform.InverseTransformPoint(((Component)val3).transform.TransformPoint(val5));
						if (!flag)
						{
							val = (val2 = val6);
							flag = true;
						}
						else
						{
							val = Vector3.Min(val, val6);
							val2 = Vector3.Max(val2, val6);
						}
					}
				}
				val2.y = Mathf.Min(val2.y, 20f);
				if (!flag || val2.x - val.x > 60f || val2.z - val.z > 60f)
				{
					return false;
				}
				byte r = 120;
				byte g = 92;
				byte b = 62;
				WearNTear component2 = prefab.GetComponent<WearNTear>();
				if ((Object)(object)component2 != (Object)null)
				{
					MaterialType materialType = component2.m_materialType;
					switch (materialType - 1)
					{
					case 0:
					case 4:
						r = 128;
						g = 126;
						b = 120;
						break;
					case 3:
						r = 190;
						g = 188;
						b = 180;
						break;
					case 1:
						r = 84;
						g = 80;
						b = 76;
						break;
					case 2:
						r = 84;
						g = 62;
						b = 44;
						break;
					}
				}
				Shapes.Register(Shape.Box(prefabHash, val.x, val.z, val2.x, val2.z, val2.y, r, g, b));
				return true;
			}
			catch (Exception ex)
			{
				SatelliteMapPlugin.Log.LogWarning((object)("Could not outline prefab " + prefabHash + ": " + ex.Message));
				return false;
			}
		}
	}
	internal sealed class GameWorldSource : IWorldSource
	{
		private readonly WorldGenerator generator;

		internal GameWorldSource(WorldGenerator generator)
		{
			this.generator = generator;
		}

		public int GetBiome(float wx, float wz)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Expected I4, but got Unknown
			return (int)generator.GetBiome(wx, wz, 0.02f, false);
		}

		public float GetBiomeHeight(int biome, float wx, float wz, out float lava)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			Color val = default(Color);
			float biomeHeight = generator.GetBiomeHeight((Biome)biome, wx, wz, ref val, false, true);
			lava = ((biome == 32) ? val.a : 0f);
			return biomeHeight;
		}
	}
	internal static class SatMap
	{
		private enum TileState
		{
			New,
			Empty,
			Waiting,
			Queued,
			Ready,
			Failed
		}

		private sealed class Tile
		{
			public int Lod;

			public int X;

			public int Z;

			public TileSpec Spec;

			public TileState State;

			public byte[] Rgba;

			public Texture2D Texture;

			public RawImage Image;

			public float LastUsed;

			public float RetryAt;

			public long ZoneStamps;

			public int ExploredVersion = -1;

			public int EmptyForVersion = -1;

			public bool Dirty;

			public float NextPoll;

			public float SortKey;

			public List<long> Zones;

			public int ZonesForVersion = -1;

			public bool InFlight;

			public float ShownAt = -1f;
		}

		private sealed class Job
		{
			public Tile Tile;

			public TileSpec Spec;

			public Dictionary<long, ZoneData> Zones;

			public long ZoneStamps;

			public int Generation;

			public byte[] Result;

			public Exception Error;

			public double Milliseconds;
		}

		private const int TileSize = 256;

		private const int CacheLimit = 120;

		private const int VisibleLimit = 96;

		private const int UploadsPerFrame = 2;

		private const float ZoneMaxAge = 180f;

		private const float RetryAfterError = 30f;

		private static readonly Dictionary<long, Tile> tiles = new Dictionary<long, Tile>();

		private static readonly Queue<Job> jobs = new Queue<Job>();

		private static readonly List<Job> finished = new List<Job>();

		private static readonly HashSet<int> foreignTried = new HashSet<int>();

		private static readonly List<Tile> visible = new List<Tile>();

		private static Thread[] workers;

		private static TileLayer layer;

		private static TileRenderer renderer;

		private static WorldGenerator rendererWorld;

		private static int generation;

		private static int exploredVersion;

		private static int queued;

		private static bool sharedShown;

		private static bool faulted;

		private static float lastLog;

		private static readonly Comparison<Tile> NearestFirst = (Tile a, Tile b) => a.SortKey.CompareTo(b.SortKey);

		internal static void Reset()
		{
			generation++;
			lock (jobs)
			{
				jobs.Clear();
				queued = 0;
			}
			lock (finished)
			{
				finished.Clear();
			}
			foreach (Tile value in tiles.Values)
			{
				if ((Object)(object)value.Texture != (Object)null)
				{
					Object.Destroy((Object)(object)value.Texture);
				}
			}
			tiles.Clear();
			if (layer != null)
			{
				layer.Destroy();
			}
			layer = null;
			renderer = null;
			rendererWorld = null;
			foreignTried.Clear();
			ZoneCache.Reset();
			faulted = false;
		}

		internal static void OnExploredChanged()
		{
			exploredVersion++;
		}

		internal static void Tick(Minimap minimap)
		{
			if (faulted)
			{
				return;
			}
			try
			{
				TickCore(minimap);
			}
			catch (Exception ex)
			{
				faulted = true;
				Hide();
				SatelliteMapPlugin.Log.LogError((object)("Detailed map picture is switched off until you re-enter the world: " + ex));
			}
		}

		private static void TickCore(Minimap minimap)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Invalid comparison between Unknown and I4
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: 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_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0361: Unknown result type (might be due to invalid IL or missing references)
			//IL_0362: Unknown result type (might be due to invalid IL or missing references)
			DetailSpec spec = MapDetail.ForLevel(SatGame.DetailLevel());
			RawImage mapImageLarge = minimap.m_mapImageLarge;
			if (!spec.Enabled || (int)minimap.m_mode != 2 || (Object)(object)mapImageLarge == (Object)null || WorldGenerator.instance == null)
			{
				Hide();
				return;
			}
			Rect uvRect = mapImageLarge.uvRect;
			Rect rect = ((Graphic)mapImageLarge).rectTransform.rect;
			if (((Rect)(ref uvRect)).width <= 0f || ((Rect)(ref rect)).width <= 1f || ((Rect)(ref rect)).height <= 1f)
			{
				Hide();
				return;
			}
			float num = (float)minimap.m_textureSize * minimap.m_pixelSize;
			float num2 = num / 2f;
			float num3 = ((Rect)(ref uvRect)).xMin * num - num2;
			float num4 = ((Rect)(ref uvRect)).xMax * num - num2;
			float num5 = ((Rect)(ref uvRect)).yMin * num - num2;
			float num6 = ((Rect)(ref uvRect)).yMax * num - num2;
			float num7 = (((Object)(object)((Graphic)mapImageLarge).canvas != (Object)null) ? ((Graphic)mapImageLarge).canvas.scaleFactor : 1f);
			float screenMetersPerPixel = (num6 - num5) / Mathf.Max(1f, ((Rect)(ref rect)).height * num7);
			float num8 = MapDetail.ResolutionFor(spec, screenMetersPerPixel);
			float num9 = MapDetail.Opacity(num6 - num5);
			if (num8 <= 0f || num9 <= 0f)
			{
				Hide();
				return;
			}
			while (TilesAcross(num3, num4, num8) * TilesAcross(num5, num6, num8) > 96)
			{
				float num10 = MapDetail.Coarser(num8);
				if (num10 <= 0f)
				{
					break;
				}
				num8 = num10;
			}
			if (layer == null || !layer.Alive)
			{
				layer = new TileLayer(((Graphic)mapImageLarge).rectTransform, MapDetail.Resolutions.Length);
			}
			layer.SetVisible(visible: true);
			EnsureRenderer();
			ZoneCache.Tick();
			Upload(minimap);
			int lod = LodOf(num8);
			float num11 = 256f * num8;
			int num12 = Mathf.FloorToInt((num3 + 32f) / num11);
			int num13 = Mathf.FloorToInt((num4 + 32f) / num11);
			int num14 = Mathf.FloorToInt((num5 + 32f) / num11);
			int num15 = Mathf.FloorToInt((num6 + 32f) / num11);
			float x = (num3 + num4) / 2f;
			float z = (num5 + num6) / 2f;
			ZoneLayers zoneLayers = MapDetail.LayersFor(spec, num8);
			Explored drawn = Explored.Of(minimap);
			if (drawn.SharedShown != sharedShown)
			{
				sharedShown = drawn.SharedShown;
				exploredVersion++;
			}
			visible.Clear();
			for (int i = num14; i <= num15; i++)
			{
				for (int j = num12; j <= num13; j++)
				{
					visible.Add(GetTile(lod, j, i, num8, zoneLayers));
				}
			}
			foreach (Tile item in visible)
			{
				item.SortKey = Distance(item, x, z);
			}
			visible.Sort(NearestFirst);
			if (visible.Count > 96)
			{
				visible.RemoveRange(96, visible.Count - 96);
			}
			foreach (Tile item2 in visible)
			{
				item2.LastUsed = Time.time;
				Advance(minimap, item2, drawn);
			}
			Layout(uvRect, rect, num, lod, num9);
			Evict();
			if (!SatelliteMapPlugin.DebugTiles.Value || !(Time.time - lastLog > 5f))
			{
				return;
			}
			lastLog = Time.time;
			int num16 = 0;
			foreach (Tile item3 in visible)
			{
				num16 += ((item3.State == TileState.Ready) ? 1 : 0);
			}
			SatelliteMapPlugin.Log.LogInfo((object)("Detail map: " + num8 + " m/px, " + screenMetersPerPixel.ToString("F2") + " m per screen px, tiles " + num16 + "/" + visible.Count + " ready, queue " + queued + ", cached " + tiles.Count));
		}

		private static int TilesAcross(float min, float max, float mpp)
		{
			float num = 256f * mpp;
			return Mathf.FloorToInt((max + 32f) / num) - Mathf.FloorToInt((min + 32f) / num) + 1;
		}

		private static void Hide()
		{
			if (layer != null)
			{
				layer.SetVisible(visible: false);
			}
		}

		private static int LodOf(float mpp)
		{
			for (int i = 0; i < MapDetail.Resolutions.Length; i++)
			{
				if (Mathf.Approximately(MapDetail.Resolutions[i], mpp))
				{
					return i;
				}
			}
			return MapDetail.Resolutions.Length - 1;
		}

		private static float Distance(Tile tile, float x, float z)
		{
			float num = tile.Spec.OriginX + tile.Spec.WorldSize / 2f - x;
			float num2 = tile.Spec.OriginZ + tile.Spec.WorldSize / 2f - z;
			return num * num + num2 * num2;
		}

		private static Tile GetTile(int lod, int x, int z, float mpp, ZoneLayers zoneLayers)
		{
			long key = ((long)lod << 56) ^ ((long)(x & 0xFFFFFFF) << 28) ^ (z & 0xFFFFFFF);
			if (!tiles.TryGetValue(key, out var value))
			{
				float num = 256f * mpp;
				value = new Tile
				{
					Lod = lod,
					X = x,
					Z = z,
					Spec = new TileSpec
					{
						OriginX = (float)x * num - 32f,
						OriginZ = (float)z * num - 32f,
						Size = 256,
						MetersPerPixel = mpp,
						Layers = zoneLayers
					}
				};
				tiles[key] = value;
			}
			else if (value.Spec.Layers != zoneLayers)
			{
				value.Spec = new TileSpec
				{
					OriginX = value.Spec.OriginX,
					OriginZ = value.Spec.OriginZ,
					Size = 256,
					MetersPerPixel = mpp,
					Layers = zoneLayers
				};
				value.Dirty = value.State == TileState.Ready;
				if (value.State == TileState.Failed)
				{
					value.State = TileState.New;
				}
			}
			return value;
		}

		private static void Advance(Minimap minimap, Tile tile, Explored drawn)
		{
			if (tile.InFlight)
			{
				return;
			}
			if (tile.State == TileState.Failed)
			{
				if (Time.time < tile.RetryAt)
				{
					return;
				}
				tile.State = TileState.New;
			}
			if (tile.State == TileState.Empty && tile.EmptyForVersion == exploredVersion)
			{
				return;
			}
			if (tile.State == TileState.New || tile.State == TileState.Empty)
			{
				if (!AnyDrawn(minimap, tile.Spec, drawn))
				{
					tile.State = TileState.Empty;
					tile.EmptyForVersion = exploredVersion;
					return;
				}
				tile.State = TileState.Waiting;
			}
			if (Time.time < tile.NextPoll)
			{
				return;
			}
			if (tile.Zones == null || tile.ZonesForVersion != exploredVersion)
			{
				tile.Zones = NeededZones(minimap, tile.Spec, drawn);
				tile.ZonesForVersion = exploredVersion;
			}
			if (tile.State == TileState.Ready && !tile.Dirty)
			{
				tile.NextPoll = Time.time + 2f + (float)(tile.X & 3) * 0.25f;
				if (CollectZones(tile, null, out var stamps) && stamps != tile.ZoneStamps)
				{
					tile.Dirty = true;
				}
				if (!tile.Dirty)
				{
					return;
				}
			}
			if (queued >= 6)
			{
				return;
			}
			Dictionary<long, ZoneData> dictionary = new Dictionary<long, ZoneData>();
			if (!CollectZones(tile, dictionary, out var stamps2))
			{
				tile.NextPoll = Time.time + 0.2f;
				return;
			}
			RegisterForeign(dictionary);
			Job item = new Job
			{
				Tile = tile,
				Spec = tile.Spec,
				Zones = dictionary,
				ZoneStamps = stamps2,
				Generation = generation
			};
			tile.Dirty = false;
			tile.InFlight = true;
			if (tile.State != TileState.Ready)
			{
				tile.State = TileState.Queued;
			}
			lock (jobs)
			{
				jobs.Enqueue(item);
				queued++;
				Monitor.Pulse(jobs);
			}
		}

		private static bool AnyDrawn(Minimap minimap, TileSpec spec, Explored drawn)
		{
			int textureSize = minimap.m_textureSize;
			float pixelSize = minimap.m_pixelSize;
			int num = Mathf.FloorToInt(spec.OriginX / pixelSize + (float)textureSize / 2f - 0.5f);
			int num2 = Mathf.FloorToInt((spec.OriginX + spec.WorldSize) / pixelSize + (float)textureSize / 2f - 0.5f) + 1;
			int num3 = Mathf.FloorToInt(spec.OriginZ / pixelSize + (float)textureSize / 2f - 0.5f);
			int num4 = Mathf.FloorToInt((spec.OriginZ + spec.WorldSize) / pixelSize + (float)textureSize / 2f - 0.5f) + 1;
			for (int i = Mathf.Max(0, num3); i <= Mathf.Min(textureSize - 1, num4); i++)
			{
				for (int j = Mathf.Max(0, num); j <= Mathf.Min(textureSize - 1, num2); j++)
				{
					if (drawn.At(j, i))
					{
						return true;
					}
				}
			}
			return false;
		}

		private static List<long> NeededZones(Minimap minimap, TileSpec spec, Explored drawn)
		{
			TileRenderer.ZoneRange(spec, out var minZoneX, out var minZoneY, out var maxZoneX, out var maxZoneY);
			List<long> list = new List<long>();
			float num = 64f;
			TileSpec tileSpec = new TileSpec
			{
				Size = 1,
				MetersPerPixel = 64f + num * 2f
			};
			for (int i = minZoneY; i <= maxZoneY; i++)
			{
				for (int j = minZoneX; j <= maxZoneX; j++)
				{
					tileSpec.OriginX = (float)j * 64f - 32f - num;
					tileSpec.OriginZ = (float)i * 64f - 32f - num;
					if (AnyDrawn(minimap, tileSpec, drawn))
					{
						list.Add(ZoneCache.Key(j, i));
					}
				}
			}
			return list;
		}

		private static bool CollectZones(Tile tile, Dictionary<long, ZoneData> target, out long stamps)
		{
			bool result = true;
			stamps = 0L;
			foreach (long zone in tile.Zones)
			{
				int zoneX = (int)(zone >> 32);
				int zoneY = (int)(zone & 0xFFFFFFFFu);
				int zoneStamp;
				ZoneData zoneData = ZoneCache.Get(zoneX, zoneY, tile.Spec.Layers, 180f, tile.SortKey, SatelliteMapApi.RevisionOf(zoneX, zoneY), out zoneStamp);
				if (zoneData == null)
				{
					result = false;
					continue;
				}
				stamps = stamps * 31 + zoneStamp;
				if (target != null)
				{
					target[zone] = zoneData;
				}
			}
			return result;
		}

		private static void RegisterForeign(Dictionary<long, ZoneData> zones)
		{
			ShapeLibrary shapes = SatGame.Shapes;
			foreach (ZoneData value in zones.Values)
			{
				foreach (ZoneObject @object in value.Objects)
				{
					if (!@object.IsLocation && shapes.Find(@object.Prefab, location: false) == null && foreignTried.Add(@object.Prefab))
					{
						SatGame.TryRegisterForeign(@object.Prefab);
					}
				}
			}
		}

		private static void EnsureRenderer()
		{
			if (renderer == null || rendererWorld != WorldGenerator.instance)
			{
				rendererWorld = WorldGenerator.instance;
				renderer = new TileRenderer(new GameWorldSource(rendererWorld), SatGame.Shapes);
			}
			if (workers == null)
			{
				workers = new Thread[2];
				for (int i = 0; i < workers.Length; i++)
				{
					workers[i] = new Thread(Work)
					{
						IsBackground = true,
						Priority = ThreadPriority.BelowNormal,
						Name = "SatelliteMap tiles " + i
					};
					workers[i].Start();
				}
			}
		}

		private static void Work()
		{
			while (true)
			{
				Job job;
				lock (jobs)
				{
					while (jobs.Count == 0)
					{
						Monitor.Wait(jobs);
					}
					job = jobs.Dequeue();
				}
				TileRenderer tileRenderer = renderer;
				if (job.Generation == generation && tileRenderer != null)
				{
					try
					{
						DateTime utcNow = DateTime.UtcNow;
						Dictionary<long, ZoneData> zones = job.Zones;
						job.Result = tileRenderer.Render(job.Spec, (int zx, int zy) => (!zones.TryGetValue(ZoneCache.Key(zx, zy), out var value)) ? null : value);
						job.Milliseconds = (DateTime.UtcNow - utcNow).TotalMilliseconds;
						if (SatelliteMapPlugin.DebugTiles.Value)
						{
							Dump(job);
						}
					}
					catch (Exception error)
					{
						job.Error = error;
					}
				}
				lock (finished)
				{
					finished.Add(job);
				}
			}
		}

		private static void Dump(Job job)
		{
			try
			{
				string text = Path.Combine(Paths.CachePath, "SatelliteMap", "tiles");
				Directory.CreateDirectory(text);
				string path = "lod" + job.Tile.Lod + "_" + job.Tile.X + "_" + job.Tile.Z + ".png";
				File.WriteAllBytes(Path.Combine(text, path), PngWriter.Encode(job.Result, job.Spec.Size, job.Spec.Size));
			}
			catch (Exception)
			{
			}
		}

		private static void Upload(Minimap minimap)
		{
			List<Job> list = null;
			lock (finished)
			{
				if (finished.Count > 0)
				{
					list = new List<Job>(finished);
					finished.Clear();
				}
			}
			if (list != null)
			{
				foreach (Job item in list)
				{
					queued = Mathf.Max(0, queued - 1);
					if (item.Generation != generation)
					{
						continue;
					}
					Tile tile = item.Tile;
					tile.InFlight = false;
					if (item.Error != null || item.Result == null)
					{
						if (tile.State != TileState.Ready)
						{
							tile.State = TileState.Failed;
							tile.RetryAt = Time.time + 30f;
						}
						SatelliteMapPlugin.Log.LogWarning((object)("Map tile " + tile.Lod + ":" + tile.X + "," + tile.Z + " failed: " + item.Error));
						continue;
					}
					tile.Rgba = item.Result;
					tile.ZoneStamps = item.ZoneStamps;
					tile.ExploredVersion = -1;
					tile.State = TileState.Ready;
					if (SatelliteMapPlugin.DebugTiles.Value)
					{
						SatelliteMapPlugin.Log.LogInfo((object)("Map tile " + tile.Lod + ":" + tile.X + "," + tile.Z + " drawn in " + item.Milliseconds.ToString("F0") + " ms, zones " + item.Zones.Count));
					}
				}
			}
			int num = 0;
			Explored explored = Explored.Of(minimap);
			int textureSize = minimap.m_textureSize;
			foreach (Tile value in tiles.Values)
			{
				if (value.Rgba != null && value.ExploredVersion != exploredVersion && !(Time.time - value.LastUsed > 1f))
				{
					if (num++ >= 2)
					{
						break;
					}
					TileMask.Apply(value.Rgba, value.Spec, ((Explored)explored).At, textureSize, minimap.m_pixelSize);
					if ((Object)(object)value.Texture != (Object)null)
					{
						Object.Destroy((Object)(object)value.Texture);
					}
					value.Texture = TileLayer.CreateTexture(value.Rgba, 256);
					value.ExploredVersion = exploredVersion;
				}
			}
		}

		private static void Layout(Rect uv, Rect rect, float worldSize, int lod, float opacity)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			foreach (Tile value in tiles.Values)
			{
				bool flag = (Object)(object)value.Texture != (Object)null && value.ExploredVersion >= 0 && value.Lod >= lod;
				Rect placed = default(Rect);
				if (flag)
				{
					placed = TileLayer.Place(uv, rect, worldSize, value.Spec.OriginX, value.Spec.OriginZ, value.Spec.WorldSize);
					flag = TileLayer.Overlaps(placed, rect);
				}
				if (!flag)
				{
					layer.Hide(value.Image);
					value.Image = null;
					continue;
				}
				if (value.Lod > lod)
				{
					value.LastUsed = Time.time;
				}
				if (value.ShownAt < 0f)
				{
					value.ShownAt = Time.time;
				}
				float num = Mathf.Clamp01((Time.time - value.ShownAt) / 0.3f);
				value.Image = layer.Show(value.Image, value.Lod, value.Texture, placed, new Color(1f, 1f, 1f, opacity * num));
			}
		}

		private static void Evict()
		{
			if (tiles.Count <= 120)
			{
				return;
			}
			List<KeyValuePair<long, Tile>> list = new List<KeyValuePair<long, Tile>>(tiles);
			list.Sort((KeyValuePair<long, Tile> a, KeyValuePair<long, Tile> b) => a.Value.LastUsed.CompareTo(b.Value.LastUsed));
			for (int num = 0; num < list.Count; num++)
			{
				if (tiles.Count <= 120)
				{
					break;
				}
				Tile value = list[num].Value;
				if (!value.InFlight && !(Time.time - value.LastUsed < 2f))
				{
					layer.Hide(value.Image);
					if ((Object)(object)value.Texture != (Object)null)
					{
						Object.Destroy((Object)(object)value.Texture);
					}
					tiles.Remove(list[num].Key);
				}
			}
		}
	}
	internal static class Snapshots
	{
		private const string Folder = "satellitemap";

		private static SnapshotStore store;

		private static long storeWorld;

		private static long failedWorld;

		private static bool failed;

		internal static SnapshotStore Current()
		{
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZNet.World == null)
			{
				return null;
			}
			long worldUID = ZNet.instance.GetWorldUID();
			if (store != null && storeWorld == worldUID)
			{
				return store;
			}
			Close();
			if (failed && failedWorld == worldUID)
			{
				return null;
			}
			try
			{
				string text = Path.Combine(Utils.GetSaveDataPath((FileSource)2), "satellitemap");
				Directory.CreateDirectory(text);
				string text2 = Path.Combine(text, SafeName(ZNet.instance.GetWorldName()) + "." + worldUID.ToString("X") + ".zones");
				store = new SnapshotStore(text2);
				storeWorld = worldUID;
				failed = false;
				SatelliteMapPlugin.Log.LogInfo((object)("Zone snapshots: " + text2 + ", " + store.Snapshots + " snapshots of " + store.Zones + " zones, " + store.Bytes / 1024 + " KB" + ((store.TornBytes > 0) ? (", dropped a torn tail of " + store.TornBytes + " bytes") : "")));
			}
			catch (Exception ex)
			{
				store = null;
				failed = true;
				failedWorld = worldUID;
				SatelliteMapPlugin.Log.LogWarning((object)("Zone snapshots are off, maps will show the world as it is now: " + ex.Message));
			}
			return store;
		}

		internal static void Flush()
		{
			if (store != null)
			{
				try
				{
					store.Flush();
				}
				catch (Exception ex)
				{
					SatelliteMapPlugin.Log.LogWarning((object)("Could not write zone snapshots: " + ex.Message));
				}
			}
		}

		internal static void Close()
		{
			if (store != null)
			{
				try
				{
					store.Dispose();
				}
				catch (Exception ex)
				{
					SatelliteMapPlugin.Log.LogWarning((object)("Could not close the zone snapshot file: " + ex.Message));
				}
				store = null;
			}
		}

		private static string SafeName(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return "world";
			}
			char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
			foreach (char oldChar in invalidFileNameChars)
			{
				name = name.Replace(oldChar, '_');
			}
			return name;
		}
	}
	internal sealed class TileLayer
	{
		private readonly RectTransform root;

		private readonly RectTransform[] lods;

		private readonly Stack<RawImage> pool = new Stack<RawImage>();

		internal bool Alive => (Object)(object)root != (Object)null;

		internal TileLayer(RectTransform mapImage, int lodCount)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Expected O, but got Unknown
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Expected O, but got Unknown
			GameObject val = new GameObject("SatelliteMap", new Type[2]
			{
				typeof(RectTransform),
				typeof(RectMask2D)
			});
			root = (RectTransform)val.transform;
			((Transform)root).SetParent((Transform)(object)mapImage, false);
			((Transform)root).SetAsFirstSibling();
			Stretch(root);
			lods = (RectTransform[])(object)new RectTransform[lodCount];
			for (int num = lodCount - 1; num >= 0; num--)
			{
				GameObject val2 = new GameObject("lod" + num, new Type[1] { typeof(RectTransform) });
				lods[num] = (RectTransform)val2.transform;
				((Transform)lods[num]).SetParent((Transform)(object)root, false);
				Stretch(lods[num]);
			}
		}

		internal void SetVisible(bool visible)
		{
			if ((Object)(object)root != (Object)null && ((Component)root).gameObject.activeSelf != visible)
			{
				((Component)root).gameObject.SetActive(visible);
			}
		}

		internal void Destroy()
		{
			pool.Clear();
			if ((Object)(object)root != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)root).gameObject);
			}
		}

		internal static Texture2D CreateTexture(byte[] rgba, int size)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: 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_0023: 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_0033: Expected O, but got Unknown
			Texture2D val = new Texture2D(size, size, (TextureFormat)4, false, false)
			{
				filterMode = (FilterMode)1,
				wrapMode = (TextureWrapMode)1,
				name = "SatelliteMap tile"
			};
			val.LoadRawTextureData(rgba);
			val.Apply(false, true);
			return val;
		}

		internal static Rect Place(Rect uv, Rect mapRect, float worldSize, float originX, float originZ, float tileWorld)
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			float num = worldSize / 2f;
			float num2 = ((originX + num) / worldSize - ((Rect)(ref uv)).xMin) / ((Rect)(ref uv)).width * ((Rect)(ref mapRect)).width;
			float num3 = ((originZ + num) / worldSize - ((Rect)(ref uv)).yMin) / ((Rect)(ref uv)).height * ((Rect)(ref mapRect)).height;
			float num4 = tileWorld / worldSize / ((Rect)(ref uv)).width * ((Rect)(ref mapRect)).width;
			float num5 = tileWorld / worldSize / ((Rect)(ref uv)).height * ((Rect)(ref mapRect)).height;
			return new Rect(num2, num3, num4, num5);
		}

		internal static bool Overlaps(Rect placed, Rect mapRect)
		{
			if (((Rect)(ref placed)).x < ((Rect)(ref mapRect)).width && ((Rect)(ref placed)).y < ((Rect)(ref mapRect)).height && ((Rect)(ref placed)).xMax > 0f)
			{
				return ((Rect)(ref placed)).yMax > 0f;
			}
			return false;
		}

		internal RawImage Show(RawImage image, int lod, Texture2D texture, Rect placed, Color tint)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)image == (Object)null)
			{
				image = ((pool.Count > 0) ? pool.Pop() : CreateImage());
				((Transform)((Graphic)image).rectTransform).SetParent((Transform)(object)lods[lod], false);
				((Component)image).gameObject.SetActive(true);
			}
			if ((object)image.texture != texture)
			{
				image.texture = (Texture)(object)texture;
			}
			((Graphic)image).color = tint;
			RectTransform rectTransform = ((Graphic)image).rectTransform;
			rectTransform.anchoredPosition = new Vector2(((Rect)(ref placed)).x - 0.25f, ((Rect)(ref placed)).y - 0.25f);
			rectTransform.sizeDelta = new Vector2(((Rect)(ref placed)).width + 0.5f, ((Rect)(ref placed)).height + 0.5f);
			return image;
		}

		internal void Hide(RawImage image)
		{
			if ((Object)(object)image != (Object)null)
			{
				((Component)image).gameObject.SetActive(false);
				image.texture = null;
				pool.Push(image);
			}
		}

		private static RawImage CreateImage()
		{
			//IL_0032: 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_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			RawImage component = new GameObject("tile", new Type[3]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer),
				typeof(RawImage)
			}).GetComponent<RawImage>();
			((Graphic)component).raycastTarget = false;
			RectTransform rectTransform = ((Graphic)component).rectTransform;
			rectTransform.anchorMin = Vector2.zero;
			rectTransform.anchorMax = Vector2.zero;
			rectTransform.pivot = Vector2.zero;
			((Transform)rectTransform).localScale = Vector3.one;
			return component;
		}

		private static void Stretch(RectTransform rt)
		{
			//IL_0001: 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_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			rt.anchorMin = Vector2.zero;
			rt.anchorMax = Vector2.one;
			rt.offsetMin = Vector2.zero;
			rt.offsetMax = Vector2.zero;
			((Transform)rt).localScale = Vector3.one;
		}
	}
	internal static class ZoneFeed
	{
		private sealed class MapRequest
		{
			public readonly List<KeyValuePair<int, int>> Zones = new List<KeyValuePair<int, int>>();

			public ZoneLayers Layers;

			public int[] Fingerprints;

			public int[] Revs;
		}

		private sealed class SurveyCall
		{
			public Action<int[], int[], int[]> Done;

			public float SentAt;
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static CoroutineHandler <0>__OnServerReceive;

			public static CoroutineHandler <1>__OnClientReceive;

			public static CoroutineHandler <2>__OnServerSurvey;

			public static CoroutineHandler <3>__OnClientSurveyed;
		}

		private const string RpcName = "zones";

		private const string SurveyRpcName = "survey";

		private const byte RequestVersion = 1;

		private const byte SurveyVersion = 1;

		private const byte FingerprintMarker = 197;

		private const byte RevisionMarker = 199;

		internal const int MaxZonesPerRequest = 96;

		private const int MaxRequestsPerPeer = 2;

		private const int MaxSurveysPerPeer = 4;

		private const float SurveyTimeout = 30f;

		private const long SelfTestSender = -9223372036854775801L;

		private static readonly FieldRef<ZDOMan, List<ZDO>[]> SectorsRef = AccessTools.FieldRefAccess<ZDOMan, List<ZDO>[]>("m_objectsBySector");

		private static readonly FieldRef<ZDOMan, Dictionary<SectorIndex, List<ZDO>>> PortalsRef = AccessTools.FieldRefAccess<ZDOMan, Dictionary<SectorIndex, List<ZDO>>>("m_portalObjects");

		private static readonly FieldRef<ZDO, Vector3> RotationRef = AccessTools.FieldRefAccess<ZDO, Vector3>("m_rotation");

		private static readonly int LocationProxyHash = StringExtensionMethods.GetStableHashCode("LocationProxy");

		private static readonly int TerrainCompilerHash = StringExtensionMethods.GetStableHashCode("_TerrainCompiler");

		private static CustomRPC rpc;

		private static CustomRPC surveyRpc;

		private static ZoneAssembler assembler;

		private static byte[] selfTestReply;

		private static readonly Dictionary<int, bool> foreignPieces = new Dictionary<int, bool>();

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

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

		private const double FrameBudgetMs = 4.0;

		private static int budgetFrame = -1;

		private static double budgetSpentMs;

		private static readonly Dictionary<int, SurveyCall> surveyCalls = new Dictionary<int, SurveyCall>();

		private static int nextSurveyId;

		internal static bool IsLocal
		{
			get
			{
				if ((Object)(object)ZNet.instance != (Object)null)
				{
					return ZNet.instance.IsServer();
				}
				return false;
			}
		}

		internal static void RegisterRpc()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//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_0040: Expected O, but got Unknown
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Expected O, but got Unknown
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Expected O, but got Unknown
			NetworkManager instance = NetworkManager.Instance;
			object obj = <>O.<0>__OnServerReceive;
			if (obj == null)
			{
				CoroutineHandler val = OnServerReceive;
				<>O.<0>__OnServerReceive = val;
				obj = (object)val;
			}
			object obj2 = <>O.<1>__OnClientReceive;
			if (obj2 == null)
			{
				CoroutineHandler val2 = OnClientReceive;
				<>O.<1>__OnClientReceive = val2;
				obj2 = (object)val2;
			}
			rpc = instance.AddRPC("zones", (CoroutineHandler)obj, (CoroutineHandler)obj2);
			NetworkManager instance2 = NetworkManager.Instance;
			object obj3 = <>O.<2>__OnServerSurvey;
			if (obj3 == null)
			{
				CoroutineHandler val3 = OnServerSurvey;
				<>O.<2>__OnServerSurvey = val3;
				obj3 = (object)val3;
			}
			object obj4 = <>O.<3>__OnClientSurveyed;
			if (obj4 == null)
			{
				CoroutineHandler val4 = OnClientSurveyed;
				<>O.<3>__OnClientSurveyed = val4;
				obj4 = (object)val4;
			}
			surveyRpc = instance2.AddRPC("survey", (CoroutineHandler)obj3, (CoroutineHandler)obj4);
		}

		internal static bool Request(List<long> zones, ZoneLayers layers, ZoneStore known, Dictionary<long, int> revs)
		{
			if (rpc == null || (Object)(object)ZNet.instance == (Object)null || ZRoutedRpc.instance == null || zones.Count == 0)
			{
				return false;
			}
			ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
			if (serverPeer == null)
			{
				return false;
			}
			rpc.SendPackage(serverPeer.m_uid, WriteRequest(zones, layers, known, revs));
			return true;
		}

		private static ZPackage WriteRequest(List<long> zones, ZoneLayers layers, ZoneStore known, Dictionary<long, int> revs)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write((byte)1);
			val.Write((byte)layers);
			val.Write((ushort)zones.Count);
			int[] array = null;
			int[] array2 = null;
			for (int i = 0; i < zones.Count; i++)
			{
				long num = zones[i];
				val.Write((short)(num >> 32));
				val.Write((short)(num & 0xFFFFFFFFu));
				if (revs != null && revs.TryGetValue(num, out var value) && value > 0)
				{
					(array2 ?? (array2 = new int[zones.Count]))[i] = value;
					continue;
				}
				ZoneData zoneData = known?.Peek(num);
				if (zoneData != null && zoneData.Rev == 0 && zoneData.Layers == layers && zoneData.ContentHash != 0)
				{
					(array ?? (array = new int[zones.Count]))[i] = zoneData.ContentHash;
				}
			}
			WriteTail(val, 197, array);
			WriteTail(val, 199, array2);
			return val;
		}

		private static void WriteTail(ZPackage pkg, byte marker, int[] values)
		{
			if (values != null)
			{
				pkg.Write(marker);
				foreach (int num in values)
				{
					pkg.Write(num);
				}
			}
		}

		private static IEnumerator OnClientReceive(long sender, ZPackage package)
		{
			try
			{
				ZoneCache.Accept(ZoneCodec.DecodePacket(package.ReadByteArray()));
			}
			catch (Exception ex)
			{
				SatelliteMapPlugin.Log.LogWarning((object)("Could not read zone descriptions: " + ex.Message));
			}
			yield break;
		}

		private static bool BudgetLeft()
		{
			int frameCount = Time.frameCount;
			if (frameCount != budgetFrame)
			{
				budgetFrame = frameCount;
				budgetSpentMs = 0.0;
			}
			return budgetSpentMs < 4.0;
		}

		private static void Spend(long startedTicks)
		{
			budgetSpentMs += (double)(Stopwatch.GetTimestamp() - startedTicks) * 1000.0 / (double)Stopwatch.Frequency;
		}

		private static IEnumerator OnServerReceive(long sender, ZPackage package)
		{
			MapRequest request = TryReadRequest(package);
			if (request == null)
			{
				yield break;
			}
			activeRequests.TryGetValue(sender, out var value);
			if (value >= 2)
			{
				yield break;
			}
			activeRequests[sender] = value + 1;
			try
			{
				List<ZoneData> zones = new List<ZoneData>(request.Zones.Count);
				List<long> unchanged = new List<long>();
				for (int i = 0; i < request.Zones.Count; i++)
				{
					while (!BudgetLeft())
					{
						yield return null;
					}
					long timestamp = Stopwatch.GetTimestamp();
					KeyValuePair<int, int> keyValuePair = request.Zones[i];
					int num = ((request.Revs != null) ? request.Revs[i] : 0);
					ZoneData zoneData = TryBuild(keyValuePair.Key, keyValuePair.Value, request.Layers, num);
					if (zoneData != null && num <= 0 && request.Fingerprints != null && request.Fingerprints[i] != 0)
					{
						zoneData.ComputeHash();
						if (zoneData.ContentHash == request.Fingerprints[i])
						{
							unchanged.Add(ZoneStore.Key(keyValuePair.Key, keyValuePair.Value));
							zoneData = null;
						}
					}
					if (zoneData != null)
					{
						zones.Add(zoneData);
					}
					Spend(timestamp);
				}
				long timestamp2 = Stopwatch.GetTimestamp();
				Reply(sender, zones, unchanged);
				Spend(timestamp2);
			}
			finally
			{
				if (activeRequests.TryGetValue(sender, out var value2) && value2 > 1)
				{
					activeRequests[sender] = value2 - 1;
				}
				else
				{
					activeRequests.Remove(sender);
				}
			}
		}

		private static MapRequest TryReadRequest(ZPackage package)
		{
			try
			{
				if (package.ReadByte() != 1)
				{
					return null;
				}
				MapRequest mapRequest = new MapRequest();
				mapRequest.Layers = (ZoneLayers)(package.ReadByte() & 7);
				int num = package.ReadUShort();
				int num2 = Math.Min(num, 96);
				for (int i = 0; i < num2; i++)
				{
					int key = package.ReadShort();
					int value = package.ReadShort();
					mapRequest.Zones.Add(new KeyValuePair<int, int>(key, value));
				}
				while (num == num2 && package.GetPos() < package.Size())
				{
					byte b = package.ReadByte();
					if (b != 197 && b != 199)
					{
						break;
					}
					int[] array = new int[num2];
					for (int j = 0; j < num2; j++)
					{
						array[j] = package.ReadInt();
					}
					if (b == 197)
					{
						mapRequest.Fingerprints = array;
					}
					else
					{
						mapRequest.Revs = array;
					}
				}
				return mapRequest;
			}
			catch (Exception ex)
			{
				SatelliteMapPlugin.Log.LogWarning((object)("Unreadable map request: " + ex.Message));
				return null;
			}
		}

		private static ZoneData TryBuild(int zoneX, int zoneY, ZoneLayers layers, int rev)
		{
			try
			{
				if (rev > 0)
				{
					ZoneData zoneData = Snapshots.Current()?.Load(zoneX, zoneY, rev);
					if (zoneData != null)
					{
						ZoneData zoneData2 = Assembler().Filter(zoneData, layers);
						zoneData2.Rev = rev;
						return zoneData2;
					}
				}
				ZoneData zoneData3 = Build(zoneX, zoneY, layers);
				zoneData3.Rev = Math.Max(0, rev);
				return zoneData3;
			}
			catch (Exception ex)
			{
				SatelliteMapPlugin.Log.LogWarning((object)("Could not describe zone " + zoneX + "," + zoneY + " for the map: " + ex));
				return null;
			}
		}

		private static ZoneAssembler Assembler()
		{
			return assembler ?? (assembler = new ZoneAssembler(SatGame.Shapes));
		}

		private static void Reply(long sender, List<ZoneData> zones, List<long> unchanged)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			try
			{
				byte[] array = ZoneCodec.Encode(zones, unchanged);
				if (sender == -9223372036854775801L)
				{
					selfTestReply = array;
					return;
				}
				ZPackage val = new ZPackage();
				val.Write(array);
				rpc.SendPackage(sender, val);
			}
			catch (Exception ex)
			{
				SatelliteMapPlugin.Log.LogWarning((object)("Could not send zone descriptions: " + ex));
			}
		}

		internal static IEnumerator SelfTest(string spec)
		{
			List<long> keys = new List<long>();
			Dictionary<long, int> direct = new Dictionary<long, int>();
			try
			{
				string[] array = spec.Split(new char[1] { ',' });
				float num = float.Parse(array[0], CultureInfo.InvariantCulture);
				float num2 = float.Parse(array[1], CultureInfo.InvariantCulture);
				float num3 = float.Parse(array[2], CultureInfo.InvariantCulture);
				DateTime utcNow = DateTime.UtcNow;
				List<ZoneData> list = new List<ZoneData>();
				int num4 = 0;
				int num5 = 0;
				for (int i = SatConst.ZoneOf(num2 - num3); i <= SatConst.ZoneOf(num2 + num3); i++)
				{
					for (int j = SatConst.ZoneOf(num - num3); j <= SatConst.ZoneOf(num + num3); j++)
					{
						ZoneData zoneData = Build(j, i, ZoneLayers.All);
						num4 += zoneData.Objects.Count;
						num5 += ((zoneData.PaintDirt != null) ? 1 : 0);
						list.Add(zoneData);
						zoneData.ComputeHash();
						keys.Add(ZoneStore.Key(j, i));
						direct[ZoneStore.Key(j, i)] = zoneData.ContentHash;
					}
				}
				byte[] array2 = ZoneCodec.Encode(list);
				double totalMilliseconds = (DateTime.UtcNow - utcNow).TotalMilliseconds;
				string text = Path.Combine(Paths.CachePath, "SatelliteMap");
				Directory.CreateDirectory(text);
				File.WriteAllBytes(Path.Combine(text, "selftest.bin"), array2);
				SatelliteMapPlugin.Log.LogInfo((object)("Self-test: " + list.Count + " zones, " + num4 + " objects, " + num5 + " zones with painted ground, packet " + array2.Length + " bytes, built in " + totalMilliseconds.ToString("F0") + " ms"));
			}
			catch (Exception ex)
			{
				SatelliteMapPlugin.Log.LogError((object)("Self-test failed: " + ex));
				yield break;
			}
			ZoneStore received = new ZoneStore();
			int requests = 0;
			int frames = 0;
			int mismatched = 0;
			int bytes = 0;
			double worstFrameMs = 0.0;
			for (int from = 0; from < keys.Count; from += 96)
			{
				ZPackage val = WriteRequest(keys.GetRange(from, Math.Min(96, keys.Count - from)), ZoneLayers.All, null, null);
				val.SetPos(0);
				selfTestReply = null;
				IEnumerator handler = OnServerReceive(-9223372036854775801L, val);
				while (true)
				{
					long timestamp = Stopwatch.GetTimestamp();
					bool num6 = handler.MoveNext();
					worstFrameMs = Math.Max(worstFrameMs, (double)(Stopwatch.GetTimestamp() - timestamp) * 1000.0 / (double)Stopwatch.Frequency);
					if (!num6)
					{
						break;
					}
					frames++;
					yield return handler.Current;
				}
				requests++;
				if (selfTestReply == null)
				{
					SatelliteMapPlugin.Log.LogError((object)("Self-test, request path: no reply to request " + requests));
					yield break;
				}
				bytes += selfTestReply.Length;
				ZonePacket zonePacket;
				try
				{
					zonePacket = ZoneCodec.DecodePacket(selfTestReply);
				}
				catch (Exception ex2)
				{
					SatelliteMapPlugin.Log.LogError((object)("Self-test, request path: unreadable reply: " + ex2));
					yield break;
				}
				foreach (ZoneData zone in zonePacket.Zones)
				{
					long key = ZoneStore.Key(zone.ZoneX, zone.ZoneY);
					if (!direct.TryGetValue(key, out var value) || value != zone.ContentHash)
					{
						mismatched++;
					}
					received.Put(zone, 0f);
				}
			}
			SatelliteMapPlugin.Log.LogInfo((object)("Self-test, request path: " + received.Count + " of " + keys.Count + " zones in " + requests + " requests, " + bytes + " bytes, spread over " + frames + " extra frames, worst slice " + worstFrameMs.ToString("F1") + " ms, fingerprint mismatches " + mismatched));
			int unchanged = 0;
			int resent = 0;
			int repeatBytes = 0;
			for (int from = 0; from < keys.Count; from += 96)
			{
				ZPackage val2 = WriteRequest(keys.GetRange(from, Math.Min(96, keys.Count - from)), ZoneLayers.All, received, null);
				val2.SetPos(0);
				selfTestReply = null;
				IEnumerator handler = OnServerReceive(-9223372036854775801L, val2);
				while (handler.MoveNext())
				{
					yield return handler.Current;
				}
				if (selfTestReply == null)
				{
					SatelliteMapPlugin.Log.LogError((object)"Self-test, repeat with fingerprints: no reply");
					yield break;
				}
				repeatBytes += selfTestReply.Length;
				ZonePacket zonePacket2 = ZoneCodec.DecodePacket(selfTestReply);
				unchanged += zonePacket2.Unchanged.Count;
				resent += zonePacket2.Zones.Count;
			}
			SatelliteMapPlugin.Log.LogInfo((object)("Self-test, repeat with fingerprints: " + unchanged + " zones unchanged, " + resent + " sent again, " + repeatBytes + " bytes"));
			Dictionary<long, int> surveyed = new Dictionary<long, int>();
			List<KeyValuePair<int, int>> list2 = new List<KeyValuePair<int, int>>();
			foreach (long item in keys)
			{
				list2.Add(new KeyValuePair<int, int>((int)(item >> 32), (int)(item & 0xFFFFFFFFu)));
			}
			DateTime surveyStart = DateTime.UtcNow;
			IEnumerator surveying = SurveyZones(list2, delegate(int[] xs, int[] ys, int[] revs)
			{
				for (int k = 0; k < xs.Length; k++)
				{
					surveyed[ZoneStore.Key(xs[k], ys[k])] = revs[k];
				}
			});
			while (surveying.MoveNext())
			{
				yield return surveying.Current;
			}
			SnapshotStore snapshots = Snapshots.Current();
			if (snapshots == null || surveyed.Count != keys.Count)
			{
				SatelliteMapPlugin.Log.LogError((object)("Self-test, snapshots: " + surveyed.Count + " of " + keys.Count + " zones got a snapshot" + ((snapshots == null) ? " (the snapshot file is not open)" : "")));
				yield break;
			}
			int wrongRev = 0;
			int wrongContent = 0;
			int snapshotBytes = 0;
			for (int from = 0; from < keys.Count; from += 96)
			{
				ZPackage val3 = WriteRequest(keys.GetRange(from, Math.Min(96, keys.Count - from)), ZoneLayers.All, null, surveyed);
				val3.SetPos(0);
				selfTestReply = null;
				IEnumerator handler = OnServerReceive(-9223372036854775801L, val3);
				while (handler.MoveNext())
				{
					yield return handler.Current;
				}
				if (selfTestReply == null)
				{
					SatelliteMapPlugin.Log.LogError((object)"Self-test, snapshots: no reply");
					yield break;
				}
				snapshotBytes += selfTestReply.Length;
				foreach (ZoneData zone2 in ZoneCodec.DecodePacket(selfTestReply).Zones)
				{
					long key2 = ZoneStore.Key(zone2.ZoneX, zone2.ZoneY);
					ZoneData zoneData2 = received.Peek(key2);
					wrongRev += ((zone2.Rev != surveyed[key2]) ? 1 : 0);
					wrongContent += ((zoneData2 == null || zoneData2.Objects.Count != zone2.Objects.Count || zoneData2.PaintDirt == null != (zone2.PaintDirt == null)) ? 1 : 0);
				}
			}
			SatelliteMapPlugin.Log.LogInfo((object)("Self-test, snapshots: " + surveyed.Count + " zones surveyed in " + (DateTime.UtcNow - surveyStart).TotalMilliseconds.ToString("F0") + " ms, the file holds " + snapshots.Snapshots + " snapshots of " + snapshots.Zones + " zones, " + snapshots.Bytes / 1024 + " KB; asked back by number: " + snapshotBytes + " bytes, wrong numbers " + wrongRev + ", content differs from the live zone in " + wrongContent));
		}

		internal static ZoneData BuildLocal(int zoneX, int zoneY, ZoneLayers layers, int rev)
		{
			return TryBuild(zoneX, zoneY, layers, rev) ?? ZoneAssembler.Begin(zoneX, zoneY, layers);
		}

		internal static bool Survey(int[] zoneXs, int[] zoneYs, Action<int[], int[], int[]> done)
		{
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Expected O, but got Unknown
			if (zoneXs == null || zoneYs == null || zoneXs.Length != zoneYs.Length || zoneXs.Length == 0 || done == null || (Object)(object)ZNet.instance == (Object)null || (Object)(object)SatelliteMapPlugin.Instance == (Object)null)
			{
				return false;
			}
			if (IsLocal)
			{
				List<KeyValuePair<int, int>> list = new List<KeyValuePair<int, int>>(zoneXs.Length);
				for (int i = 0; i < zoneXs.Length; i++)
				{
					list.Add(new KeyValuePair<int, int>(zoneXs[i], zoneYs[i]));
				}
				((MonoBehaviour)SatelliteMapPlugin.Instance).StartCoroutine(SurveyZones(list, done));
				return true;
			}
			ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
			if (surveyRpc == null || serverPeer == null)
			{
				return false;
			}
			float time = Time.time;
			List<int> list2 = null;
			foreach (KeyValuePair<int, SurveyCall> surveyCall in surveyCalls)
			{
				if (time - surveyCall.Value.SentAt > 30f)
				{
					(list2 ?? (list2 = new List<int>())).Add(surveyCall.Key);
				}
			}
			if (list2 != null)
			{
				foreach (int item in list2)
				{
					surveyCalls.Remove(item);
				}
			}
			for (int j = 0; j < zoneXs.Length; j += 96)
			{
				int num = Math.Min(96, zoneXs.Length - j);
				int num2 = ++nextSurveyId;
				surveyCalls[num2] = new SurveyCall
				{
					Done = done,
					SentAt = time
				};
				ZPackage val = new ZPackage();
				val.Write((byte)1);
				val.Write(num2);
				val.Write((ushort)num);
				for (int k = j; k < j + num; k++)
				{
					val.Write((short)zoneXs[k]);
					val.Write((short)zoneYs[k]);
				}
				surveyRpc.SendPackage(serverPeer.m_uid, val);
			}
			return true;
		}

		internal static void ForgetSurveys()
		{
			surveyCalls.Clear();
		}

		private static IEnumerator OnServerSurvey(long sender, ZPackage package)
		{
			int id = 0;
			List<KeyValuePair<int, int>> list = new List<KeyValuePair<int, int>>();
			try
			{
				if (package.ReadByte() != 1)
				{
					yield break;
				}
				id = package.ReadInt();
				int num = Math.Min((int)package.ReadUShort(), 96);
				for (int i = 0; i < num; i++)
				{
					int key = package.ReadShort();
					int value = package.ReadShort();
					list.Add(new KeyValuePair<int, int>(key, value));
				}
			}
			catch (Exception ex)
			{
				SatelliteMapPlugin.Log.LogWarning((object)("Unreadable survey request: " + ex.Message));
				yield break;
			}
			activeSurveys.TryGetValue(sender, out var value2);
			if (value2 >= 4)
			{
				yield break;
			}
			activeSurveys[sender] = value2 + 1;
			try
			{
				IEnumerator work = SurveyZones(list, delegate(int[] xs, int[] ys, int[] revs)
				{
					//IL_0000: Unknown result type (might be due to invalid IL or missing references)
					//IL_0006: Expected O, but got Unknown
					ZPackage val = new ZPackage();
					val.Write((byte)1);
					val.Write(id);
					val.Write((ushort)xs.Length);
					for (int j = 0; j < xs.Length; j++)
					{
						val.Write((short)xs[j]);
						val.Write((short)ys[j]);
						val.Write(revs[j]);
					}
					surveyRpc.SendPackage(sender, val);
				});
				while (work.MoveNext())
				{
					yield return work.Current;
				}
			}
			finally
			{
				if (activeSurveys.TryGetValue(sender, out var value3) && value3 > 1)
				{
					activeSurveys[sender] = value3 - 1;
				}
				else
				{
					activeSurveys.Remove(sender);
				}
			}
		}

		private static IEnumerator SurveyZones(List<KeyValuePair<int, int>> zones, Action<int[], int[], int[]> done)
		{
			List<int> xs = new List<int>(zones.Count);
			List<int> ys = new List<int>(zones.Count);
			List<int> revs = new List<int>(zones.Count);
			int nowMinutes = (int)(DateTimeOffset.UtcNow.ToUnixTimeSeconds() / 60);
			for (int i = 0; i < zones.Count; i++)
			{
				while (!BudgetLeft())
				{
					yield return null;
				}
				long timestamp = Stopwatch.GetTimestamp();
				try
				{
					SnapshotStore snapshotStore = Snapshots.Current();
					KeyValuePair<int, int> keyValuePair = zones[i];
					if (snapshotStore != null && keyValuePair.Key > -256 && keyValuePair.Value > -256 && keyValuePair.Key < 256 && keyValuePair.Value < 256)
					{
						int item = snapshotStore.Survey(Build(keyValuePair.Key, keyValuePair.Value, ZoneLayers.All), nowMinutes);
						xs.Add(keyValuePair.Key);
						ys.Add(keyValuePair.Value);
						revs.Add(item);
					}
				}
				catch (Exception ex)
				{
					SatelliteMapPlugin.Log.LogWarning((object)("Could not take a snapshot of zone " + zones[i].Key + "," + zones[i].Value + ": " + ex.Message));
				}
				Spend(timestamp);
			}
			Snapshots.Flush();
			try
			{
				done(xs.ToArray(), ys.ToArray(), revs.ToArray());
			}
			catch (Exception ex2)
			{
				SatelliteMapPlugin.Log.LogWarning((object)("Survey result was not accepted: " + ex2));
			}
		}

		private static IEnumerator OnClientSurveyed(long sender, ZPackage package)
		{
			try
			{
				if (package.ReadByte() == 1)
				{
					int key = package.ReadInt();
					int num = package.ReadUShort();
					int[] array = new int[num];
					int[] array2 = new int[num];
					int[] array3 = new int[num];
					for (int i = 0; i < num; i++)
					{
						array[i] = package.ReadShort();
						array2[i] = package.ReadShort();
						array3[i] = package.ReadInt();
					}
					if (surveyCalls.TryGetValue(key, out var value))
					{
						surveyCalls.Remove(key);
						value.Done(array, array2, array3);
					}
				}
			}
			catch (Exception ex)
			{
				SatelliteMapPlugin.Log.LogWarning((object)("Could not read the survey reply: " + ex));
			}
			yield break;
		}

		private static ZoneData Build(int zoneX, int zoneY, ZoneLayers layers)
		{
			//IL_004c: 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_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			ZoneData zoneData = ZoneAssembler.Begin(zoneX, zoneY, layers);
			ZDOMan instance = ZDOMan.instance;
			if (instance == null || zoneX <= -256 || zoneY <= -256 || zoneX >= 256 || zoneY >= 256)
			{
				return zoneData;
			}
			if (assembler == null)
			{
				assembler = new ZoneAssembler(SatGame.Shapes);
			}
			SectorIndex val = ZoneSystem.SectorToIndex(zoneX, zoneY);
			List<ZDO>[] array = SectorsRef.Invoke(instance);
			if (array != null && val.Sector < array.Length)
			{
				AddAll(zoneData, array[val.Sector]);
			}
			Dictionary<SectorIndex, List<ZDO>> dictionary = PortalsRef.Invoke(instance);
			if (dictionary != null && dictionary.TryGetValue(val, out var value))
			{
				AddAll(zoneData, value);
			}
			return zoneData;
		}

		private static void AddAll(ZoneData zone, List<ZDO> objects)
		{
			//IL_0038: 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_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: 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_010f: Unknown result type (might be due to invalid IL or missing references)
			if (objects == null)
			{
				return;
			}
			bool flag = (zone.Layers & ZoneLayers.Structures) != 0;
			for (int i = 0; i < objects.Count; i++)
			{
				ZDO val = objects[i];
				if (val == null || !val.IsValid())
				{
					continue;
				}
				int prefab = val.GetPrefab();
				Vector3 position = val.GetPosition();
				if (prefab == TerrainCompilerHash)
				{
					if (flag)
					{
						ReadTerrain(zone, val);
					}
					continue;
				}
				float y = RotationRef.Invoke(val).y;
				if (prefab == LocationProxyHash)
				{
					int num = val.GetInt(ZDOVars.s_location, 0);
					if (num != 0)
					{
						assembler.Add(zone, num, isLocation: true, position.x, position.y, position.z, y, 1f);
					}
				}
				else if (assembler.Knows(prefab))
				{
					assembler.Add(zone, prefab, isLocation: false, position.x, position.y, position.z, y, ScaleOf(val));
				}
				else if (flag && IsForeignPiece(prefab))
				{
					assembler.AddUnknown(zone, prefab, position.x, position.y, position.z, y, ScaleOf(val));
				}
			}
		}

		private static void ReadTerrain(ZoneData zone, ZDO zdo)
		{
			try
			{
				zone.ReadTerrainComp(zdo.GetByteArray(ZDOVars.s_TCData, (byte[])null));
			}
			catch (Exception ex)
			{
				SatelliteMapPlugin.Log.LogWarning((object)("Terrain data of zone " + zone.ZoneX + "," + zone.ZoneY + " is unreadable: " + ex.Message));
			}
		}

		private static float ScaleOf(ZDO zdo)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: 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_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			Vector3 vec = zdo.GetVec3(ZDOVars.s_scaleHash, Vector3.zero);
			if (vec != Vector3.zero)
			{
				return vec.x;
			}
			return zdo.GetFloat(ZDOVars.s_scaleScalarHash, 1f);
		}

		private static bool IsForeignPiece(int prefab)
		{
			if (foreignPieces.TryGetValue(prefab, out var value))
			{
				return value;
			}
			value = false;
			try
			{
				value = SatGame.IsForeignPiece(prefab);
			}
			catch (Exception)
			{
			}
			foreignPieces[prefab] = value;
			return value;
		}
	}
	internal static class ZoneCache
	{
		private const float RequestTimeout = 12f;

		private const float ProbeEvery = 60f;

		private const int LocalZonesPerFrame = 24;

		private static readonly ZoneStore store = new ZoneStore();

		private static readonly ZoneWants wants = new ZoneWants();

		private static readonly HashSet<long> inFlight = new HashSet<long>();

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

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

		private static float inFlightSince;

		private static float lastProbe;

		private static int timeouts;

		private static long probeKey;

		private static bool hasProbeKey;

		internal static bool ServerSilent { get; private set; }

		internal static long Key(int zoneX, int zoneY)
		{
			return ZoneStore.Key(zoneX, zoneY);
		}

		internal static void Reset()
		{
			store.Clear();
			wants.Clear();
			inFlight.Clear();
			wantedRevs.Clear();
			inFlightRevs.Clear();
			ZoneFeed.ForgetSurveys();
			timeouts = 0;
			hasProbeKey = false;
			ServerSilent = false;
		}

		internal static ZoneData Get(int zoneX, int zoneY, ZoneLayers layers, float maxAge, float priority, int rev, out int zoneStamp)
		{
			long key = Key(zoneX, zoneY);
			float time = Time.time;
			rev = Math.Max(0, rev);
			float age;
			ZoneData zoneData = store.Find(key, time, out age);
			if (ServerSilent)
			{
				probeKey = key;
				hasProbeKey = true;
				if (zoneData == null)
				{
					zoneData = ZoneAssembler.Begin(zoneX, zoneY, ZoneLayers.All);
					store.Put(zoneData, time);
				}
				zoneStamp = ZoneStore.StampOf(zoneData);
				return zoneData;
			}
			if (zoneData != null && (zoneData.Layers & layers) == layers && zoneData.Rev == rev)
			{
				zoneStamp = ZoneStore.StampOf(zoneData);
				if (rev == 0 && age > maxAge)
				{
					Want(key, layers, priority, time, rev);
				}
				return zoneData;
			}
			zoneStamp = 0;
			Want(key, layers, priority, time, rev);
			return null;
		}

		private static void Want(long key, ZoneLayers layers, float priority, float now, int rev)
		{
			if (!inFlight.Contains(key))
			{
				wants.Add(key, layers, priority, now);
				if (rev > 0)
				{
					wantedRevs[key] = rev;
				}
				else
				{
					wantedRevs.Remove(key);
				}
			}
		}

		internal static void Tick()
		{
			float time = Time.time;
			if (inFlight.Count > 0 && time - inFlightSince > 12f)
			{
				inFlight.Clear();
				inFlightRevs.Clear();
				if (!ServerSilent && ++timeouts >= 2)
				{
					ServerSilent = true;
					lastProbe = time;
					wants.Clear();
					SatelliteMapPlugin.Log.LogWarning((object)"The server does not answer map requests (SatelliteMap is not installed there?). The map stays detailed, but without buildings, paths and forest.");
				}
			}
			store.Trim(time);
			if (ServerSilent)
			{
				Probe(time);
			}
			else
			{
				if (wants.Count == 0 || inFlight.Count > 0)
				{
					return;
				}
				bool isLocal = ZoneFeed.IsLocal;
				ZoneLayers layers;
				List<long> list = wants.Take(isLocal ? 24 : 96, time, out layers);
				if (list.Count == 0)
				{
					return;
				}
				inFlightRevs.Clear();
				foreach (long item in list)
				{
					if (wantedRevs.TryGetValue(item, out var value))
					{
						inFlightRevs[item] = value;
						wantedRevs.Remove(item);
					}
				}
				if (isLocal)
				{
					ZonePacket zonePacket = new ZonePacket();
					foreach (long item2 in list)
					{
						inFlightRevs.TryGetValue(item2, out var value2);
						zonePacket.Zones.Add(ZoneFeed.BuildLocal((int)(item2 >> 32), (int)(item2 & 0xFFFFFFFFu), layers, value2));
					}
					Accept(zonePacket);
				}
				else
				{
					if (!ZoneFeed.Request(list, layers, store, inFlightRevs))
					{
						return;
					}
					foreach (long item3 in list)
					{
						inFlight.Add(item3);
					}
					inFlightSince = time;
				}
			}
		}

		private static void Probe(float now)
		{
			if (hasProbeKey && inFlight.Count <= 0 && !(now - lastProbe < 60f) && !ZoneFeed.IsLocal)
			{
				lastProbe = now;
				if (ZoneFeed.Request(new List<long> { probeKey }, ZoneLayers.All, null, null))
				{
					inFlight.Add(probeKey);
					inFlightSince = now;
				}
			}
		}

		internal static void Accept(ZonePacket packet)
		{
			float time = Time.time;
			timeouts = 0;
			inFlight.Clear();
			if (ServerSilent)
			{
				ServerSilent = false;
				store.Clear();
				SatelliteMapPlugin.Log.LogInfo((object)"The server answers map requests again: buildings, paths and forest are back on the map.");
			}
			foreach (ZoneData zone in packet.Zones)
			{
				if (zone.ContentHash == 0)
				{
					zone.ComputeHash();
				}
				if (zone.Rev == 0 && inFlightRevs.TryGetValue(ZoneStore.Key(zone.ZoneX, zone.ZoneY), out var value))
				{
					zone.Rev = value;
				}
				store.Put(zone, time);
			}
			inFlightRevs.Clear();
			foreach (long item in packet.Unchanged)
			{
				store.Confirm(item, time);
			}
		}
	}
}
namespace SatelliteMap.Render
{
	public struct DetailSpec
	{
		public bool Enabled;

		public float FinestMetersPerPixel;

		public ZoneLayers Layers;
	}
	public static class MapDetail
	{
		public const int None = 0;

		public const int Max = 3;

		public static readonly float[] Resolutions = new float[4] { 0.5f, 1f, 2f, 4f };

		public const float FadeOutStart = 3200f;

		public const float FadeOutEnd = 3900f;

		public static DetailSpec ForLevel(int level)
		{
			return level switch
			{
				1 => new DetailSpec
				{
					Enabled = true,
					FinestMetersPerPixel = 2f,
					Layers = ZoneLayers.Structures
				}, 
				2 => new DetailSpec
				{
					Enabled = true,
					FinestMetersPerPixel = 1f,
					Layers = (ZoneLayers.Structures | ZoneLayers.Trees)
				}, 
				3 => new DetailSpec
				{
					Enabled = true,
					FinestMetersPerPixel = 0.5f,
					Layers = ZoneLayers.All
				}, 
				_ => new DetailSpec
				{
					Enabled = false,
					FinestMetersPerPixel = 0f,
					Layers = ZoneLayers.None
				}, 
			};
		}

		public static float ResolutionFor(DetailSpec spec, float screenMetersPerPixel)
		{
			if (!spec.Enabled)
			{
				return 0f;
			}
			float num = Resolutions[0];
			for (int i = 0; i < Resolutions.Length; i++)
			{
				if (Resolutions[i] <= screenMetersPerPixel * 1.5f)
				{
					num = Resolutions[i];
				}
			}
			if (!(num < spec.FinestMetersPerPixel))
			{
				return num;
			}
			return spec.FinestMetersPerPixel;
		}

		public static float Opacity(float viewHeightMeters)
		{
			float num = (3900f - viewHeightMeters) / 700f;
			if (!(num < 0f))
			{
				if (!(num > 1f))
				{
					return num;
				}
				return 1f;
			}
			return 0f;
		}

		public static float Coarser(float metersPerPixel)
		{
			for (int i = 0; i < Resolutions.Length - 1; i++)
			{
				if (Resolutions[i] >= metersPerPixel * 0.99f)
				{
					return Resolutions[i + 1];
				}
			}
			return 0f;
		}

		public static ZoneLayers LayersFor(DetailSpec spec, float metersPerPixel)
		{
			return (ZoneLayers)(((metersPerPixel <= 0.5f) ? 7 : 3) & (int)spec.Layers);
		}
	}
	internal static class ObjectPainter
	{
		private struct Placed
		{
			public ZoneObject Object;

			public Shape Shape;

			public float Top;
		}

		public static void Paint(ShapeLibrary shapes, TileSpec spec, Func<int, int, ZoneData> zones, TerrainField field, float minX, float minZ, int w, float mpp, float[] surface, float[] shade, float[] cr, float[] cg, float[] cb)
		{
			float num = minX + (float)w * mpp;
			float num2 = minZ + (float)w * mpp;
			TileRenderer.ZoneRange(spec, out var minZoneX, out var minZoneY, out var maxZoneX, out var maxZoneY);
			List<Placed> list = new List<Placed>();
			for (int i = minZoneY; i <= maxZoneY; i++)
			{
				for (int j = minZoneX; j <= maxZoneX; j++)
				{
					ZoneData zoneData = zones(j, i);
					if (zoneData == null)
					{
						continue;
					}
					foreach (ZoneObject @object in zoneData.Objects)
					{
						if (@object.Y > 1000f)
						{
							continue;
						}
						Shape shape = shapes.Find(@object.Prefab, @object.IsLocation);
						if (shape != null)
						{
							float num3 = ((@object.Scale > 0f) ? @object.Scale : 1f);
							float num4 = shape.Radius * num3;
							if ((ShapeLibrary.LayerOf(shape.Kind, num4) & spec.Layers) != ZoneLayers.None && !(@object.X + num4 < minX) && !(@object.X - num4 > num) && !(@object.Z + num4 < minZ) && !(@object.Z - num4 > num2))
							{
								list.Add(new Placed
								{
									Object = @object,
									Shape = shape,
									Top = @object.Y + shape.MaxTop * num3
								});
							}
						}
					}
				}
			}
			list.Sort((Placed a, Placed b) => a.Top.CompareTo(b.Top));
			foreach (Placed item in list)
			{
				if (!IsStructure(item.Shape))
				{
					Draw(item.Object, item.Shape, minX, minZ, w, mpp, surface, surface, shade, cr, cg, cb);
				}
			}
			float[] array = null;
			foreach (Placed item2 in list)
			{
				if (!IsStructure(item2.Shape))
				{
					continue;
				}
				if (array == null)
				{
					array = new float[surface.Length];
					for (int num5 = 0; num5 < array.Length; num5++)
					{
						array[num5] = float.MinValue;
					}
				}
				Draw(item2.Object, item2.Shape, minX, minZ, w, mpp, array, surface, shade, cr, cg, cb);
			}
		}

		private static bool IsStructure(Shape shape)
		{
			if (shape.Kind != ShapeKind.Piece)
			{
				return shape.Kind == ShapeKind.Location;
			}
			return true;
		}

		private static void Draw(ZoneObject o, Shape shape, float minX, float minZ, int w, float mpp, float[] depth, float[] surface, float[] shade, float[] cr, float[] cg, float[] cb)
		{
			float num = ((o.Scale > 0f) ? o.Scale : 1f);
			ShapeLevel shapeLevel = shape.LevelFor(mpp / num);
			float num2 = shape.Radius * num;
			int num3 = Math.Max(0, (int)Math.Floor((o.X - num2 - minX) / mpp));
			int num4 = Math.Min(w - 1, (int)Math.Ceiling((o.X + num2 - minX) / mpp));
			int num5 = Math.Max(0, (int)Math.Floor((o.Z - num2 - minZ) / mpp));
			int num6 = Math.Min(w - 1, (int)Math.Ceiling((o.Z + num2 - minZ) / mpp));
			if (num4 < num3 || num6 < num5)
			{
				return;
			}
			double num7 = (double)o.Yaw * Math.PI / 180.0;
			float num8 = (float)Math.Cos(num7);
			float num9 = (float)Math.Sin(num7);
			float num10 = shapeLevel.PixelsPerMeter / num;
			float num11 = SatStyle.LightX * num8 - SatStyle.LightZ * num9;
			float num12 = SatStyle.LightX * num9 + SatStyle.LightZ * num8;
			float num13 = num / shapeLevel.PixelsPerMeter;
			float num14 = ((shape.Kind == ShapeKind.Tree || shape.Kind == ShapeKind.Small) ? 1.25f : 1f);
			for (int i = num5; i <= num6; i++)
			{
				float num15 = minZ + ((float)i + 0.5f) * mpp - o.Z;
				for (int j = num3; j <= num4; j++)
				{
					float num16 = minX + ((float)j + 0.5f) * mpp - o.X;
					float num17 = (num16 * num8 - num15 * num9) * num10 + shapeLevel.OriginX - 0.5f;
					float num18 = (num16 * num9 + num15 * num8) * num10 + shapeLevel.OriginZ - 0.5f;
					if (num17 < -1f || num18 < -1f || num17 > (float)shapeLevel.Width || num18 > (float)shapeLevel.Height)
					{
						continue;
					}
					int num19 = (int)Math.Floor(num17);
					int num20 = (int)Math.Floor(num18);
					float num21 = num17 - (float)num19;
					float num22 = num18 - (float)num20;
					float num23 = 0f;
					float num24 = 0f;
					float num25 = 0f;
					float num26 = 0f;
					float num27 = 0f;
					float num28 = 0f;
					float num29 = 0f;
					float num30 = 0f;
					float num31 = 0f;
					float num32 = 0f;
					float num33 = 0f;
					float num34 = 0f;
					float num35 = 0f;
					for (int k = 0; k < 4; k++)
					{
						int num36 = num19 + (k & 1);
						int num37 = num20 + (k >> 1);
						if (num36 < 0 || num37 < 0 || num36 >= shapeLevel.Width || num37 >= shapeLevel.Height)
						{
							continue;
						}
						int num38 = num37 * shapeLevel.Width + num36;
						float num39 = (int)shapeLevel.Rgba[num38 * 4 + 3];
						if (!(num39 <= 0f))
						{
							float num40 = (((k & 1) == 0) ? (1f - num21) : num21) * ((k >> 1 == 0) ? (1f - num22) : num22) * num39;
							num23 += num40;
							num24 += (float)(int)shapeLevel.Rgba[num38 * 4] * num40;
							num25 += (float)(int)shapeLevel.Rgba[num38 * 4 + 1] * num40;
							num26 += (float)(int)shapeLevel.Rgba[num38 * 4 + 2] * num40;
							num27 += shapeLevel.Top[num38] * num40;
							if ((k & 1) == 0)
							{
								num28 += shapeLevel.Top[num38] * num40;
								num32 += num40;
							}
							else
							{
								num29 += shapeLevel.Top[num38] * num40;
								num33 += num40;
							}
							if (k >> 1 == 0)
							{
								num30 += shapeLevel.Top[num38] * num40;
								num34 += num40;
							}
							else
							{
								num31 += shapeLevel.Top[num38] * num40;
								num35 += num40;
							}
						}
					}
					if (num23 < 20f)
					{
						continue;
					}
					float num41 = num23 / 255f;
					float num42 = o.Y + num27 / num23 * num;
					int num43 = i * w + j;
					if (num42 < depth[num43] - 0.05f)
					{
						continue;
					}
					float num44 = num14 / (num23 * 255f);
					float num45 = 1f;
					if (num32 > 0f && num33 > 0f && num34 > 0f && num35 > 0f)
					{
						float num46 = (num29 / num33 - num28 / num32) * num / num13;
						float num47 = (num31 / num35 - num30 / num34) * num / num13;
						float num48 = (float)Math.Sqrt(num46 * num46 + num47 * num47);
						if (num48 > 2.5f)
						{
							num46 *= 2.5f / num48;
							num47 *= 2.5f / num48;
						}
						float num49 = 0f - num46;
						float num50 = 0f - num47;
						float num51 = 1f / (float)Math.Sqrt(num49 * num49 + 1f + num50 * num50);
						num45 = 1f + 0.9f * ((num49 * num11 + SatStyle.LightY + num50 * num12) * num51 - SatStyle.LightY);
						num45 = ((num45 < 0.55f) ? 0.55f : ((num45 > 1.3f) ? 1.3f : num45));
					}
					float num52 = ((num41 > 1f) ? 1f : num41);
					cr[num43] += (num24 * num44 - cr[num43]) * num52;
					cg[num43] += (num25 * num44 - cg[num43]) * num52;
					cb[num43] += (num26 * num44 - cb[num43]) * num52;
					shade[num43] += (num45 - shade[num43]) * num52;
					if (num41 > 0.5f)
					{
						depth[num43] = num42;
						surface[num43] = num42;
					}
				}
			}
		}
	}
	public static class PngWriter
	{
		private static uint[] crcTable;

		public static byte[] Encode(byte[] rgba, int width, int height)
		{
			using MemoryStream memoryStream = new MemoryStream();
			memoryStream.Write(new byte[8] { 137, 80, 78, 71, 13, 10, 26, 10 }, 0, 8);
			byte[] array = new byte[13];
			WriteBE(array, 0, width);
			WriteBE(array, 4, height);
			array[8] = 8;
			array[9] = 6;
			Chunk(memoryStream, "IHDR", array);
			using (MemoryStream memoryStream2 = new MemoryStream())
			{
				memoryStream2.WriteByte(120);
				memoryStream2.WriteByte(156);
				uint num = 1u;
				uint num2 = 0u;
				using (DeflateStream deflateStream = new DeflateStream(memoryStream2, CompressionLevel.Fastest, leaveOpen: true))
				{
					byte[] array2 = new byte[width * 4 + 1];
					for (int num3 = height - 1; num3 >= 0; num3--)
					{
						Buffer.BlockCopy(rgba, num3 * width * 4, array2, 1, width * 4);
						deflateStream.Write(array2, 0, array2.Length);
						for (int i = 0; i < array2.Length; i++)
						{
							num = (num + array2[i]) % 65521;
							num2 = (num2 + num) % 65521;
						}
					}
				}
				byte[] array3 = new byte[4];
				WriteBE(array3, 0, (int)((num2 << 16) | num));
				memoryStream2.Write(array3, 0, 4);
				Chunk(memoryStream, "IDAT", memoryStream2.ToArray());
			}
			Chunk(memoryStream, "IEND", new byte[0]);
			return memoryStream.ToArray();
		}

		private static void Chunk(Stream png, string type, byte[] data)
		{
			byte[] array = new byte[4];
			WriteBE(array, 0, data.Length);
			png.Write(array, 0, 4);
			byte[] array2 = new byte[4 + data.Length];
			for (int i = 0; i < 4; i++)
			{
				array2[i] = (byte)type[i];
			}
			Buffer.BlockCopy(data, 0, array2, 4, data.Length);
			png.Write(array2, 0, array2.Length);
			byte[] array3 = new byte[4];
			WriteBE(array3, 0, (int)Crc(array2));
			png.Write(array3, 0, 4);
		}

		private static uint Crc(byte[] bytes)
		{
			if (crcTable == null)
			{
				uint[] array = new uint[256];
				for (uint num = 0u; num < 256; num++)
				{
					uint num2 = num;
					for (int i = 0; i < 8; i++)
					{
						num2 = (((num2 & 1) != 0) ? (0xEDB88320u ^ (num2 >> 1)) : (num2 >> 1));
					}
					array[num] = num2;
				}
				crcTable = array;
			}
			uint num3 = uint.MaxValue;
			for (int j = 0; j < bytes.Length; j++)
			{
				num3 = crcTable[(num3 ^ bytes[j]) & 0xFF] ^ (num3 >> 8);
			}
			return num3 ^ 0xFFFFFFFFu;
		}

		private static void WriteBE(byte[] target, int offset, int value)
		{
			target[offset] = (byte)(value >> 24);
			target[offset + 1] = (byte)(value >> 16);
			target[offset + 2] = (byte)(value >> 8);
			target[offset + 3] = (byte)value;
		}
	}
	public struct Rgb
	{
		public float R;

		public float G;

		public float B;

		public Rgb(float r, float g, float b)
		{
			R = r;
			G = g;
			B = b;
		}

		public static Rgb Bytes(int r, int g, int b)
		{
			return new Rgb((float)r / 255f, (float)g / 255f, (float)b / 255f);
		}

		public static Rgb Lerp(Rgb a, Rgb b, float t)
		{
			t = ((t < 0f) ? 0f : ((t > 1f) ? 1f : t));
			return new Rgb(a.R + (b.R - a.R) * t, a.G + (b.G - a.G) * t, a.B + (b.B - a.B) * t);
		}

		public Rgb Scale(float k)
		{
			return new Rgb(R * k, G * k, B * k);
		}
	}
	public static class SatStyle
	{
		public static readonly Rgb MeadowsLow = Rgb.Bytes(96, 132, 58);

		public static readonly Rgb MeadowsHigh = Rgb.Bytes(128, 146, 72);

		public static readonly Rgb ForestLow = Rgb.Bytes(58, 86, 46);

		public static readonly Rgb ForestHigh = Rgb.Bytes(78, 98, 56);

		public static readonly Rgb SwampLow = Rgb.Bytes(70, 74, 44);

		public static readonly Rgb SwampHigh = Rgb.Bytes(86, 88, 52);

		public static readonly Rgb Snow = Rgb.Bytes(232, 238, 244);

		public static readonly Rgb SnowShade = Rgb.Bytes(196, 208, 224);

		public static readonly Rgb PlainsLow = Rgb.Bytes(168, 156, 84);

		public static readonly Rgb PlainsHigh = Rgb.Bytes(186, 170, 100);

		public static readonly Rgb MistLow = Rgb.Bytes(62, 66, 74);

		public static readonly Rgb MistHigh = Rgb.Bytes(84, 80, 98);

		public static readonly Rgb AshLow = Rgb.Bytes(84, 58, 50);

		public static readonly Rgb AshHigh = Rgb.Bytes(112, 72, 58);

		public static readonly Rgb Lava = Rgb.Bytes(236, 104, 24);

		public static readonly Rgb NorthLow = Rgb.Bytes(214, 226, 238);

		public static readonly Rgb NorthHigh = Rgb.Bytes(240, 244, 248);

		public static readonly Rgb SeaBed = Rgb.Bytes(150, 146, 112);

		public static readonly Rgb Rock = Rgb.Bytes(116, 112, 104);

		public static readonly Rgb RockDark = Rgb.Bytes(84, 82, 80);

		public static readonly Rgb Sand = Rgb.Bytes(182, 170, 128);

		public static readonly Rgb Dirt = Rgb.Bytes(126, 100, 66);

		public static readonly Rgb Paved = Rgb.Bytes(146, 144, 138);

		public static readonly Rgb Cultivated = Rgb.Bytes(88, 66, 44);

		public static readonly Rgb WaterShallow = Rgb.Bytes(96, 158, 160);

		public static readonly Rgb WaterMid = Rgb.Bytes(44, 100, 136);

		public static readonly Rgb WaterDeep = Rgb.Bytes(22, 52, 92);

		public static readonly Rgb WaterSwamp = Rgb.Bytes(66, 76, 48);

		public static readonly Rgb Foam = Rgb.Bytes(226, 236, 232);

		public static readonly float LightX = -0.375f;

		public static readonly float LightY = 0.848f;

		public static readonly float LightZ = 0.375f;

		public const float ReliefExaggeration = 1.5f;

		public const float ReliefContrast = 1.25f;

		public const float ShadeMin = 0.66f;

		public const float ShadeMax = 1.3f;

		public const float ShadeSideSoftening = 0.65f;

		public const float ShadowReach = 16f;

		public const float ShadowDarkness = 0.3f;

		public const float ShadowSoftness = 1.2f;

		public const float FoliageGain = 1.25f;

		public static float Hash(int x, int z)
		{
			int num = x * 374761393 + z * 668265263;
			int num2 = (num ^ (num >>> 13)) * 1274126177;
			return (float)(uint)((num2 ^ (num2 >>> 16)) & 0xFFFF) / 65535f;
		}

		public static float ValueNoise(float x, float z)
		{
			int num = (int)Math.Floor(x);
			int num2 = (int)Math.Floor(z);
			float num3 = x - (float)num;
			float num4 = z - (float)num2;
			num3 = num3 * num3 * (3f - 2f * num3);
			num4 = num4 * num4 * (3f - 2f * num4);
			float num5 = Hash(num, num2);
			float num6 = Hash(num + 1, num2);
			float num7 = Hash(num, num2 + 1);
			float num8 = Hash(num + 1, num2 + 1);
			return (num5 + (num6 - num5) * num3) * (1f - num4) + (num7 + (num8 - num7) * num3) * num4;
		}

		public static Rgb Ground(int biome, float height, float slope, float mottle, float lava)
		{
			float num = height - 30f;
			float t = Clamp01(num / 40f);
			float num2 = 0.75f;
			float num3 = 1.5f;
			Rgb a;
			switch (biome)
			{
			case 1:
				a = Rgb.Lerp(MeadowsLow, MeadowsHigh, t);
				break;
			case 8:
				a = Rgb.Lerp(ForestLow, ForestHigh, t);
				break;
			case 2:
				a = Rgb.Lerp(SwampLow, SwampHigh, Clamp01(num / 6f));
				break;
			case 4:
				a = Rgb.Lerp(SnowShade, Snow, Clamp01((num - 50f) / 60f));
				num2 = 0.9f;
				num3 = 1.6f;
				break;
			case 16:
				a = Rgb.Lerp(PlainsLow, PlainsHigh, t);
				break;
			case 512:
				a = Rgb.Lerp(MistLow, MistHigh, t);
				num2 = 0.6f;
				break;
			case 32:
				a = Rgb.Lerp(AshLow, AshHigh, t);
				a = Rgb.Lerp(a, Lava, Clamp01(lava * 1.4f));
				break;
			case 64:
				a = Rgb.Lerp(NorthLow, NorthHigh, t);
				break;
			default:
				a = SeaBed;
				break;
			}
			float num4 = Clamp01((slope - num2) / (num3 - num2));
			if (num4 > 0f)
			{
				a = Rgb.Lerp(a, Rgb.Lerp(Rock, RockDark, mottle), num4);
			}
			if (biome != 2 && biome != 64 && biome != 4 && biome != 32)
			{
				float num5 = 1f - Clamp01((num - 0.3f) / 1.1f);
				a = Rgb.Lerp(a, Sand, num5 * 0.85f);
			}
			return a.Scale(0.9f + mottle * 0.2f);
		}

		public static float Clamp01(float v)
		{
			if (!(v < 0f))
			{
				if (!(v > 1f))
				{
					return v;
				}
				return 1f;
			}
			return 0f;
		}
	}
	public enum ShapeKind : byte
	{
		Piece,
		Tree,
		Rock,
		Small,
		Decor,
		Location
	}
	public sealed class ShapeLevel
	{
		public int Width;

		public int Height;

		public float PixelsPerMeter;

		public float OriginX;

		public float OriginZ;

		public byte[] Rgba;

		public float[] Top;
	}
	public sealed class Shape
	{
		public int Hash;

		public ShapeKind Kind;

		public float Radius;

		public float MaxTop;

		internal int Width;

		internal int Height;

		internal float PixelsPerMeter;

		internal float OriginX;

		internal float OriginZ;

		internal float MinHeight;

		internal float HeightStep;

		internal byte[] Index;

		internal byte[] HeightQ;

		internal ShapeLibrary Owner;

		private ShapeLevel[] levels;

		public ShapeLevel LevelFor(float metersPerTexel)
		{
			ShapeLevel[] array = Levels();
			int num = 0;
			for (int i = 1; i < array.Length; i++)
			{
				if (1f / array[i].PixelsPerMeter <= metersPerTexel * 1.01f)
				{
					num = i;
				}
			}
			return array[num];
		}

		private ShapeLevel[] Levels()
		{
			if (levels != null)
			{
				return levels;
			}
			lock (this)
			{
				if (levels != null)
				{
					return levels;
				}
				ShapeLevel shapeLevel = new ShapeLevel
				{
					Width = Width,
					Height = Height,
					PixelsPerMeter = PixelsPerMeter,
					OriginX = OriginX,
					OriginZ = OriginZ,
					Rgba = new byte[Width * Height * 4],
					Top = new float[Width * Height]
				};
				byte[] palette = Owner.Palette;
				for (int i = 0; i < Width * Height; i++)
				{
					int num = Index[i];
					if (num != 0)
					{
						int num2 = (num - 1) * 3;
						shapeLevel.Rgba[i * 4] = palette[num2];
						shapeLevel.Rgba[i * 4 + 1] = palette[num2 + 1];
						shapeLevel.Rgba[i * 4 + 2] = palette[num2 + 2];
						shapeLevel.Rgba[i * 4 + 3] = byte.MaxValue;
						shapeLevel.Top[i] = MinHeight + (float)(HeightQ[i] - 1) * HeightStep;
					}
				}
				Index = null;
				HeightQ = null;
				levels = Chain(shapeLevel);
				return levels;
			}
		}

		private static ShapeLevel[] Chain(ShapeLevel level)
		{
			List<ShapeLevel> list = new List<ShapeLevel> { level };
			while (level.Width > 2 && level.Height > 2 && list.Count < 6)
			{
				level = Halve(level);
				list.Add(level);
			}
			return list.ToArray();
		}

		public static Shape Box(int hash, float minX, float minZ, float maxX, float maxZ, float top, byte r, byte g, byte b)
		{
			int num = Math.Max(1, (int)Math.Ceiling((maxX - minX) * 4f));
			int num2 = Math.Max(1, (int)Math.Ceiling((maxZ - minZ) * 4f));
			ShapeLevel shapeLevel = new ShapeLevel
			{
				Width = num,
				Height = num2,
				PixelsPerMeter = 4f,
				OriginX = (0f - minX) * 4f,
				OriginZ = (0f - minZ) * 4f,
				Rgba = new byte[num * num2 * 4],
				Top = new float[num * num2]
			};
			for (int i = 0; i < num * num2; i++)
			{
				shapeLevel.Rgba[i * 4] = r;
				shapeLevel.Rgba[i * 4 + 1] = g;
				shapeLevel.Rgba[i * 4 + 2] = b;
				shapeLevel.Rgba[i * 4 + 3] = byte.MaxValue;
				shapeLevel.Top[i] = top;
			}
			float num3 = Math.Max(Math.Abs(minX), Math.Abs(maxX));
			float num4 = Math.Max(Math.Abs(minZ), Math.Abs(maxZ));
			return new Shape
			{
				Hash = hash,
				Kind = ShapeKind.Piece,
				Radius = (float)Math.Sqrt(num3 * num3 + num4 * num4),
				MaxTop = top,
				levels = Chain(shapeLevel)
			};
		}

		private static ShapeLevel Halve(ShapeLevel src)
		{
			ShapeLevel shapeLevel = new ShapeLevel
			{
				Width = (src.Width + 1) / 2,
				Height = (src.Height + 1) / 2,
				PixelsPerMeter = src.PixelsPerMeter / 2f,
				OriginX = src.OriginX / 2f,
				OriginZ = src.OriginZ / 2f
			};
			shapeLevel.Rgba = new byte[shapeLevel.Width * shapeLevel.Height * 4];
			shapeLevel.Top = new float[shapeLevel.Width * shapeLevel.Height];
			for (int i = 0; i < shapeLevel.Height; i++)
			{
				for (int j = 0; j < shapeLevel.Width; j++)
				{
					float num = 0f;
					float num2 = 0f;
					float num3 = 0f;
					float num4 = 0f;
					float num5 = 0f;
					for (int k = 0; k < 2; k++)
					{
						for (int l = 0; l < 2; l++)
						{
							int num6 = j * 2 + l;
							int num7 = i * 2 + k;
							if (num6 < src.Width && num7 < src.Height)
							{
								int num8 = num7 * src.Width + num6;
								float num9 = (int)src.Rgba[num8 * 4 + 3];
								num += (float)(int)src.Rgba[num8 * 4] * num9;
								num2 += (float)(int)src.Rgba[num8 * 4 + 1] * num9;
								num3 += (float)(int)src.Rgba[num8 * 4 + 2] * num9;
								num5 += src.Top[num8] * num9;
								num4 += num9;
							}
						}
					}
					int num10 = i * shapeLevel.Width + j;
					if (num4 > 0f)
					{
						shapeLevel.Rgba[num10 * 4] = (byte)(num / num4 + 0.5f);
						shapeLevel.Rgba[num10 * 4 + 1] = (byte)(num2 / num4 + 0.5f);
						shapeLevel.Rgba[num10 * 4 + 2] = (byte)(num3 / num4 + 0.5f);
						shapeLevel.Rgba[num10 * 4 + 3] = (byte)(num4 / 4f + 0.5f);
						shapeLevel.Top[num10] = num5 / num4;
					}
				}
			}
			return shapeLevel;
		}
	}
	public sealed class ShapeLibrary
	{
		public const float MaxRadius = 48f;

		internal byte[] Palette;

		private volatile Dictionary<int, Shape> prefabs = new Dictionary<int, Shape>();

		private readonly Dictionary<int, Shape> locations = new Dictionary<int, Shape>();

		private readonly object writeLock = new object();

		private readonly HashSet<int> gamePrefabs = new HashSet<int>();

		public int Count => prefabs.Count + locations.Count;

		public bool IsGamePrefab(int hash)
		{
			return gamePrefabs.Contains(hash);
		}

		public void Register(Shape shape)
		{
			lock (writeLock)
			{
				Dictionary<int, Shape> dictionary = new Dictionary<int, Shape>(prefabs);
				dictionary[shape.Hash] = shape;
				prefabs = dictionary;
			}
		}

		public Shape Find(int hash, bool location)
		{
			if (!(location ? locations : prefabs).TryGetValue(hash, out var value))
			{
				return null;
			}
			return value;
		}

		public static ZoneLayers LayerOf(ShapeKind kind, float radius)
		{
			if (kind == ShapeKind.Piece || kind == ShapeKind.Location)
			{
				return ZoneLayers.Structures;
			}
			if (!(radius >= 1f))
			{
				return ZoneLayers.Small;
			}
			return ZoneLayers.Trees;
		}

		public static ShapeLib