Decompiled source of PortalPreview v1.2.0

PortalPreview.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.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.PostProcessing;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("PortalPreview")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.2.0.0")]
[assembly: AssemblyInformationalVersion("1.2.0+6965af1c9c6d33c80e1744df0459b6ff7f2e322e")]
[assembly: AssemblyProduct("PortalPreview")]
[assembly: AssemblyTitle("PortalPreview")]
[assembly: AssemblyVersion("1.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 PortalPreview
{
	internal sealed class BoundedArchiveWriter : IDisposable
	{
		private sealed class Job
		{
			internal MemoryStream Payload;

			internal int Capacity;

			internal Task<bool> Task;
		}

		internal const long DefaultCapacityLimit = 33554432L;

		internal const int DefaultJobLimit = 8;

		internal const int DefaultWorkers = 4;

		private readonly long _capacityLimit;

		private readonly int _jobLimit;

		private readonly SemaphoreSlim _workers;

		private readonly List<Job> _jobs = new List<Job>();

		private readonly HashSet<string> _scheduled = new HashSet<string>(StringComparer.Ordinal);

		private long _retainedCapacity;

		private ExceptionDispatchInfo _failure;

		private bool _disposed;

		private int _activeWorkers;

		private int _peakWorkers;

		internal int WrittenCount { get; private set; }

		internal long PeakRetainedCapacity { get; private set; }

		internal int PeakJobs { get; private set; }

		internal int PeakWorkers => Volatile.Read(in _peakWorkers);

		internal long RetainedCapacity => _retainedCapacity;

		internal int PendingJobs => _jobs.Count;

		internal BoundedArchiveWriter(long capacityLimit = 33554432L, int jobLimit = 8, int workers = 4)
		{
			if (capacityLimit <= 0 || jobLimit <= 0 || workers <= 0)
			{
				throw new ArgumentOutOfRangeException();
			}
			_capacityLimit = capacityLimit;
			_jobLimit = jobLimit;
			_workers = new SemaphoreSlim(workers, workers);
		}

		internal void Enqueue(string hash, MemoryStream payload, Func<Stream, bool> write)
		{
			bool flag = false;
			try
			{
				if (_disposed)
				{
					throw new ObjectDisposedException("BoundedArchiveWriter");
				}
				ReapCompleted();
				if (_failure != null)
				{
					Drain();
				}
				if (_scheduled.Contains(hash))
				{
					return;
				}
				int capacity = payload.Capacity;
				if (capacity > _capacityLimit)
				{
					Drain();
					_scheduled.Add(hash);
					payload.Position = 0L;
					if (write(payload))
					{
						WrittenCount++;
					}
					return;
				}
				while (_retainedCapacity + capacity > _capacityLimit || _jobs.Count >= _jobLimit)
				{
					WaitForAny();
					if (_failure != null)
					{
						Drain();
					}
				}
				_scheduled.Add(hash);
				Job job = new Job
				{
					Payload = payload,
					Capacity = capacity
				};
				job.Task = Task.Run(delegate
				{
					_workers.Wait();
					int num = Interlocked.Increment(ref _activeWorkers);
					int num2;
					do
					{
						num2 = Volatile.Read(in _peakWorkers);
					}
					while (num > num2 && Interlocked.CompareExchange(ref _peakWorkers, num, num2) != num2);
					try
					{
						job.Payload.Position = 0L;
						return write(job.Payload);
					}
					finally
					{
						job.Payload.Dispose();
						job.Payload = null;
						Interlocked.Decrement(ref _activeWorkers);
						_workers.Release();
					}
				});
				flag = true;
				_jobs.Add(job);
				_retainedCapacity += capacity;
				PeakRetainedCapacity = Math.Max(PeakRetainedCapacity, _retainedCapacity);
				PeakJobs = Math.Max(PeakJobs, _jobs.Count);
			}
			finally
			{
				if (!flag)
				{
					payload.Dispose();
				}
			}
		}

		private void ReapCompleted()
		{
			for (int num = _jobs.Count - 1; num >= 0; num--)
			{
				Job job = _jobs[num];
				if (job.Task.IsCompleted)
				{
					try
					{
						if (job.Task.GetAwaiter().GetResult())
						{
							WrittenCount++;
						}
					}
					catch (Exception source)
					{
						if (_failure == null)
						{
							_failure = ExceptionDispatchInfo.Capture(source);
						}
					}
					_retainedCapacity -= job.Capacity;
					_jobs.RemoveAt(num);
				}
			}
		}

		private void WaitForAny()
		{
			Task[] array = new Task[_jobs.Count];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = _jobs[i].Task;
			}
			Task.WaitAny(array);
			ReapCompleted();
		}

		internal void Drain()
		{
			while (_jobs.Count > 0)
			{
				WaitForAny();
			}
			_failure?.Throw();
		}

		public void Dispose()
		{
			if (_disposed)
			{
				return;
			}
			try
			{
				Drain();
			}
			finally
			{
				_disposed = true;
				_workers.Dispose();
			}
		}
	}
	internal static class CaptureFilterChecks
	{
		internal static void Verify(int layer, Action<bool, string> require)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			//IL_006e: 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_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Expected O, but got Unknown
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			int num = LayerMask.NameToLayer("ghost");
			require(num >= 0, "Valheim's placement-ghost layer is available");
			List<Object> owned = new List<Object>();
			try
			{
				Vector3 val = default(Vector3);
				((Vector3)(ref val))..ctor(0f, 14000f, 0f);
				Mesh mesh = new Mesh();
				owned.Add((Object)(object)mesh);
				mesh.vertices = (Vector3[])(object)new Vector3[3]
				{
					Vector3.zero,
					Vector3.right,
					Vector3.up
				};
				mesh.triangles = new int[3] { 0, 1, 2 };
				mesh.RecalculateBounds();
				mesh.RecalculateNormals();
				Material material = new Material(PortalProjection.FindShader("Standard"));
				owned.Add((Object)(object)material);
				Func<string, Vector3, int, Renderer> obj = delegate(string name, Vector3 position, int sourceLayer)
				{
					//IL_0001: Unknown result type (might be due to invalid IL or missing references)
					//IL_0007: Expected O, but got Unknown
					//IL_0020: Unknown result type (might be due to invalid IL or missing references)
					GameObject val8 = new GameObject(name);
					owned.Add((Object)(object)val8);
					val8.layer = sourceLayer;
					val8.transform.position = position;
					val8.AddComponent<MeshFilter>().sharedMesh = mesh;
					MeshRenderer obj2 = val8.AddComponent<MeshRenderer>();
					((Renderer)obj2).sharedMaterial = material;
					return (Renderer)(object)obj2;
				};
				Renderer val2 = obj("Capture filter ordinary piece", val, 0);
				Renderer val3 = obj("Capture filter nearby ghost", val + Vector3.right * 2f, num);
				Renderer val4 = obj("Capture filter distant ghost", val + Vector3.forward * 50f, num);
				Light val5 = ((Component)val2).gameObject.AddComponent<Light>();
				val5.type = (LightType)2;
				val5.range = 5f;
				Light val6 = ((Component)val3).gameObject.AddComponent<Light>();
				val6.type = (LightType)2;
				val6.range = 5f;
				using PortalSnapshot portalSnapshot = PortalSnapshot.Capture(val, Quaternion.identity, 8f, layer, (Renderer[])(object)new Renderer[3] { val2, val3, val4 }, (Light[])(object)new Light[2] { val5, val6 });
				require(portalSnapshot.MeshCount == 1, "placement ghost does not become frozen scene geometry");
				require(portalSnapshot.LightCount == 1, "placement ghost light is excluded while ordinary light is kept");
				HashSet<Renderer> hashSet = (HashSet<Renderer>)AccessTools.Field(typeof(PortalSnapshot), "_backdropExcludedSources").GetValue(portalSnapshot);
				require(hashSet.Contains(val3) && hashSet.Contains(val4), "near and beyond-radius placement ghosts are both hidden from the panorama");
				require(!val3.forceRenderingOff && !val4.forceRenderingOff && ((Behaviour)val6).enabled && ((Behaviour)val5).enabled, "capture restores the live ghost renderers and lights");
				require(!portalSnapshot.ShouldExcludeCaptureSource(((Component)val2).transform), "ordinary world geometry remains eligible");
				if ((Object)(object)Player.m_localPlayer != (Object)null)
				{
					require(portalSnapshot.ShouldExcludeCaptureSource(((Component)Player.m_localPlayer).transform), "local player root is excluded independently of capture distance");
					Light[] componentsInChildren = ((Component)Player.m_localPlayer).GetComponentsInChildren<Light>(true);
					foreach (Light val7 in componentsInChildren)
					{
						require(portalSnapshot.ShouldExcludeCaptureSource(((Component)val7).transform), "local player's attached lights are excluded");
					}
				}
			}
			finally
			{
				foreach (Object item in owned)
				{
					if (item != (Object)null)
					{
						Object.Destroy(item);
					}
				}
			}
		}
	}
	internal static class CaptureRetry
	{
		internal const float MinimumRadius = 32f;

		internal const float MaximumDelay = 600f;

		internal static float Delay(int failures)
		{
			return (float)Math.Min(600.0, 30.0 * Math.Pow(2.0, Math.Max(0, failures - 1)));
		}

		internal static float SmallerRadius(float radius)
		{
			return Math.Max(32f, radius * 0.75f);
		}
	}
	internal sealed class SnapshotLimitException : InvalidOperationException
	{
		internal SnapshotLimitException(string message)
			: base(message)
		{
		}
	}
	internal struct Lighting
	{
		public string Environment;

		public Texture AuroraGradient;

		public Quaternion SunRotation;

		public Vector3 SkyboxSunDir;

		public Color SunColor;

		public Color FogColor;

		public Color SunFogColor;

		public Color Ambient;

		public float SunIntensity;

		public float FogDensity;

		public float Wet;

		public float Aurora;

		internal bool Differs(in Lighting other)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: 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_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: 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)
			if (Environment != other.Environment)
			{
				return true;
			}
			if (Quaternion.Angle(SunRotation, other.SunRotation) >= 10f)
			{
				return true;
			}
			if (Far(SunColor * SunIntensity, other.SunColor * other.SunIntensity) || Far(Ambient, other.Ambient) || Far(FogColor, other.FogColor) || Far(SunFogColor, other.SunFogColor))
			{
				return true;
			}
			if (Mathf.Abs(FogDensity - other.FogDensity) > 0.15f * Mathf.Max(new float[3] { FogDensity, other.FogDensity, 0.0001f }))
			{
				return true;
			}
			if (!(Mathf.Abs(Aurora - other.Aurora) > 0.05f))
			{
				return Wet != other.Wet;
			}
			return true;
		}

		private static bool Far(Color a, Color b)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			if (!(Mathf.Abs(a.r - b.r) > 0.03f) && !(Mathf.Abs(a.g - b.g) > 0.03f))
			{
				return Mathf.Abs(a.b - b.b) > 0.03f;
			}
			return true;
		}
	}
	internal static class DestinationLighting
	{
		private static readonly List<EnvEntry> Available = new List<EnvEntry>();

		internal static bool TryCompute(Vector3 position, out Lighting lighting)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			lighting = default(Lighting);
			EnvMan instance = EnvMan.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)ZNet.instance == (Object)null || WorldGenerator.instance == null || Character.InInterior(position))
			{
				return false;
			}
			EnvSetup val = Weather(instance, position);
			if (val == null)
			{
				return false;
			}
			lighting = At(instance, val, instance.GetDayFraction());
			return true;
		}

		internal static string CompareWithLive(Vector3 position)
		{
			//IL_0023: 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_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			EnvMan instance = EnvMan.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_dirLight == (Object)null)
			{
				return "no EnvMan";
			}
			if (!TryCompute(position, out var lighting))
			{
				return "no lighting computed here (interior or no weather)";
			}
			Light dirLight = instance.m_dirLight;
			EnvSetup currentEnvironment = instance.GetCurrentEnvironment();
			return "weather computed " + lighting.Environment + ", live " + ((currentEnvironment != null) ? currentEnvironment.m_name : "none") + "; " + $"sun angle {Quaternion.Angle(lighting.SunRotation, ((Component)dirLight).transform.rotation):F1} deg, " + $"intensity {lighting.SunIntensity:F3}/{dirLight.intensity:F3}, colour {lighting.SunColor}/{dirLight.color}; " + $"ambient {lighting.Ambient}/{RenderSettings.ambientLight}; fog {lighting.FogColor}/{RenderSettings.fogColor}, " + string.Format("density {0:F5}/{1:F5}; aurora {2:F2}/{3:F2}; ", lighting.FogDensity, RenderSettings.fogDensity, lighting.Aurora, Shader.GetGlobalFloat("_AuroraStrength")) + $"probe difference {ProbeDifference(PortalSnapshot.FlatProbe(RenderSettings.ambientLight), RenderSettings.ambientProbe):F4}";
		}

		internal static float ProbeDifference(SphericalHarmonicsL2 a, SphericalHarmonicsL2 b)
		{
			float num = 0f;
			for (int i = 0; i < 3; i++)
			{
				for (int j = 0; j < 9; j++)
				{
					num = Mathf.Max(num, Mathf.Abs(((SphericalHarmonicsL2)(ref a))[i, j] - ((SphericalHarmonicsL2)(ref b))[i, j]));
				}
			}
			return num;
		}

		internal static Lighting At(EnvMan env, EnvSetup setup, float fraction)
		{
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: 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_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: 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_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0242: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_024c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0252: Unknown result type (might be due to invalid IL or missing references)
			//IL_0258: Unknown result type (might be due to invalid IL or missing references)
			//IL_025e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0263: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Unknown result type (might be due to invalid IL or missing references)
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_0275: Unknown result type (might be due to invalid IL or missing references)
			//IL_027b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0282: Unknown result type (might be due to invalid IL or missing references)
			//IL_0287: Unknown result type (might be due to invalid IL or missing references)
			//IL_028c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0292: Unknown result type (might be due to invalid IL or missing references)
			//IL_0298: Unknown result type (might be due to invalid IL or missing references)
			//IL_029d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_020a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0222: Unknown result type (might be due to invalid IL or missing references)
			//IL_0228: Unknown result type (might be due to invalid IL or missing references)
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Unknown result type (might be due to invalid IL or missing references)
			//IL_0239: Unknown result type (might be due to invalid IL or missing references)
			//IL_023e: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0302: Unknown result type (might be due to invalid IL or missing references)
			//IL_0336: Unknown result type (might be due to invalid IL or missing references)
			//IL_033c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0342: Unknown result type (might be due to invalid IL or missing references)
			//IL_0347: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02de: Unknown result type (might be due to invalid IL or missing references)
			float num = (((Object)(object)env != (Object)null) ? env.m_sunHorizonTransitionL : 0.02f);
			float num2 = (((Object)(object)env != (Object)null) ? env.m_sunHorizonTransitionH : 0.08f);
			float num3 = Mathf.Pow(Mathf.Max(1f - Mathf.Clamp01(fraction / 0.25f), Mathf.Clamp01((fraction - 0.75f) / 0.25f)), 0.5f);
			float num4 = Mathf.Pow(Mathf.Clamp01(1f - Mathf.Abs(fraction - 0.5f) / 0.25f), 0.5f);
			float num5 = Mathf.Min(Mathf.Clamp01(1f - (fraction - 0.26f) / (0f - num)), Mathf.Clamp01(1f - (fraction - 0.26f) / num2));
			float num6 = Mathf.Min(Mathf.Clamp01(1f - (fraction - 0.74f) / (0f - num2)), Mathf.Clamp01(1f - (fraction - 0.74f) / num));
			float num7 = num4 + num3 + num5 + num6;
			if (num7 > 0f)
			{
				num4 /= num7;
				num3 /= num7;
				num5 /= num7;
				num6 /= num7;
			}
			Lighting result = new Lighting
			{
				Environment = setup.m_name,
				AuroraGradient = (Texture)(object)setup.m_auroraGradientTexture
			};
			Quaternion val = Quaternion.Euler(-90f + setup.m_sunAngle, 0f, 0f) * Quaternion.Euler(0f, -90f, 0f) * Quaternion.Euler(-90f + 360f * fraction, 0f, 0f);
			result.SkyboxSunDir = -(val * Vector3.forward);
			result.SunIntensity = setup.m_lightIntensityDay * num4 + setup.m_lightIntensityNight * num3;
			if (num3 > 0f)
			{
				val *= Quaternion.Euler(180f, 0f, 0f);
			}
			result.SunRotation = val;
			Color val2 = setup.m_sunColorNight * num3;
			if (num4 > 0f)
			{
				val2 += setup.m_sunColorDay * num4 + setup.m_sunColorMorning * num5 + setup.m_sunColorEvening * num6;
			}
			result.SunColor = val2;
			result.FogColor = setup.m_fogColorNight * num3 + setup.m_fogColorDay * num4 + setup.m_fogColorMorning * num5 + setup.m_fogColorEvening * num6;
			Color val3 = setup.m_fogColorSunNight * num3;
			if (num4 > 0f)
			{
				val3 += setup.m_fogColorSunDay * num4 + setup.m_fogColorSunMorning * num5 + setup.m_fogColorSunEvening * num6;
			}
			result.SunFogColor = Color.Lerp(result.FogColor, val3, Mathf.Clamp01(Mathf.Max(num3, num4) * 3f));
			result.FogDensity = setup.m_fogDensityNight * num3 + setup.m_fogDensityDay * num4 + setup.m_fogDensityMorning * num5 + setup.m_fogDensityEvening * num6;
			result.Ambient = Color.Lerp(setup.m_ambColorNight, setup.m_ambColorDay, num4);
			result.Wet = (setup.m_isWet ? 1f : 0f);
			result.Aurora = setup.m_auroraIntensityDay * num4 + setup.m_auroraIntensityEvening * num6 + setup.m_auroraIntensityMorning * num5 + setup.m_auroraIntensityNight * num3;
			return result;
		}

		private static EnvSetup Weather(EnvMan env, Vector3 position)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			BiomeSector biomeSector = WorldGenerator.instance.GetBiomeSector(position, false);
			string text = null;
			foreach (AltBiome altBiome in biomeSector.AltBiomes)
			{
				if (!string.IsNullOrEmpty(altBiome.m_forceEnvironment))
				{
					text = altBiome.m_forceEnvironment;
					break;
				}
			}
			if (text == null)
			{
				text = PersistentEventEnvironment(position);
			}
			if (text != null)
			{
				foreach (EnvSetup environment in env.m_environments)
				{
					if (environment.m_name == text)
					{
						return environment;
					}
				}
				return null;
			}
			long num = (long)ZNet.instance.GetTimeSeconds() / env.m_environmentDuration;
			List<EnvEntry> availableEnvironments = env.GetAvailableEnvironments(biomeSector);
			if (availableEnvironments == null || availableEnvironments.Count == 0)
			{
				return null;
			}
			Available.Clear();
			Available.AddRange(availableEnvironments);
			bool flag = WorldGenerator.IsAshlands(position.x, position.z);
			bool flag2 = WorldGenerator.IsDeepnorth(position.x, position.y);
			State state = Random.state;
			try
			{
				Random.InitState((int)num);
				EnvSetup result = SelectWeighted(Available);
				foreach (EnvEntry item in Available)
				{
					if (item.m_ashlandsOverride && flag)
					{
						result = item.m_env;
					}
					if (item.m_deepnorthOverride && flag2)
					{
						result = item.m_env;
					}
				}
				return result;
			}
			finally
			{
				Random.state = state;
			}
		}

		private static string PersistentEventEnvironment(Vector3 position)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			PersistentEventSystem instance = PersistentEventSystem.instance;
			if ((Object)(object)instance == (Object)null || instance.m_activePersistentEvents == null)
			{
				return null;
			}
			foreach (ActivePersistentEvent item in instance.m_activePersistentEvents.list)
			{
				Vector3 val = item.position - position;
				if (((Vector3)(ref val)).sqrMagnitude < item.radius * item.radius)
				{
					string environmentOverride = item.Source.GetEnvironmentOverride(position);
					return string.IsNullOrEmpty(environmentOverride) ? null : environmentOverride;
				}
			}
			return null;
		}

		private static EnvSetup SelectWeighted(List<EnvEntry> environments)
		{
			float num = 0f;
			foreach (EnvEntry environment in environments)
			{
				if (!environment.m_ashlandsOverride && !environment.m_deepnorthOverride)
				{
					num += environment.m_weight;
				}
			}
			float num2 = Random.Range(0f, num);
			float num3 = 0f;
			foreach (EnvEntry environment2 in environments)
			{
				if (!environment2.m_ashlandsOverride && !environment2.m_deepnorthOverride)
				{
					num3 += environment2.m_weight;
					if (num3 >= num2)
					{
						return environment2.m_env;
					}
				}
			}
			EnvEntry val = environments[environments.Count - 1];
			if (!val.m_ashlandsOverride && !val.m_deepnorthOverride)
			{
				return val.m_env;
			}
			return null;
		}
	}
	internal static class FrozenPortals
	{
		private sealed class Endpoint
		{
			public readonly PortalViewBudget.Entry<Endpoint> ViewState = new PortalViewBudget.Entry<Endpoint>();

			public ZDOID Id;

			public ZDOID Target;

			public TeleportWorld Portal;

			public PortalSnapshot Snapshot;

			public bool SnapshotVerified;

			public float RetryAt;

			public float LastUsed;

			public bool Reported;

			private string _lastError;

			public string WaitReason;

			public bool Refresh;

			public bool FirstLinkCapture;

			public bool SkipArchiveLoad;

			public float StableAt;

			public float NextZdoRequestAt;

			public int Failures;

			public float CaptureRadius;

			public float CapturedAt = float.NegativeInfinity;

			public CaptureWhen When;

			public bool Observed;

			public float NoArrivalHoldUntil;

			public ulong? Fingerprint;

			public bool FingerprintKnown;

			public string LastError
			{
				get
				{
					return _lastError;
				}
				set
				{
					_lastError = value;
					DiskLoadFailed = false;
				}
			}

			public bool DiskLoadFailed { get; private set; }

			public void FailDiskLoad(string message)
			{
				LastError = "disk load failed: " + message;
				DiskLoadFailed = true;
			}
		}

		private enum CaptureWhen
		{
			Now,
			WhenShown,
			AtTeleport
		}

		private sealed class ArchivedScene
		{
			public string Id;

			public string Path;

			public Vector3 Position;

			public Quaternion Rotation;

			public int Version;

			public DateTime WrittenUtc;
		}

		private struct ViewCandidate
		{
			public Endpoint Endpoint;

			public PortalSnapshot Snapshot;

			public PortalView View;
		}

		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static ConsoleEvent <>9__49_0;

			public static Comparison<Endpoint> <>9__101_0;

			internal void <Initialize>b__49_0(ConsoleEventArgs args)
			{
				//IL_046d: Unknown result type (might be due to invalid IL or missing references)
				string text = ((args.Length > 1) ? args[1].ToLowerInvariant() : "status");
				switch (text)
				{
				case "on":
					Clear();
					PortalPreviewPlugin.Cfg.Enabled.Value = true;
					_status = "enabled; waiting for a loaded wooden or stone portal";
					break;
				case "off":
					PortalPreviewPlugin.Cfg.Enabled.Value = false;
					Clear();
					break;
				case "clear":
					Clear();
					_status = "RAM cache cleared; saved snapshots retained on disk";
					break;
				case "backdropcheck":
				{
					if (RefreshNearby() == 0)
					{
						args.Context.AddString("No connected portal within 24 m to capture.");
						return;
					}
					string text2 = Path.Combine(Paths.BepInExRootPath, "cache", "PortalPreview", "diagnostics", "backdrop-" + DateTime.Now.ToString("yyyyMMdd-HHmmss"));
					Directory.CreateDirectory(text2);
					PortalSnapshot.BackdropDiagnostics = text2;
					_viewDiagnostics = text2;
					_diagnosticsUntil = Time.realtimeSinceStartup + 60f;
					args.Context.AddString("The next capture of a nearby portal writes a panorama and view comparison to " + text2);
					break;
				}
				case "refresh":
					RefreshNearby();
					break;
				case "log":
					if (args.Length != 3 || (args[2] != "on" && args[2] != "off"))
					{
						args.Context.AddString("Usage: frozenportal log on|off");
						return;
					}
					PortalPreviewPlugin.Verbose = args[2] == "on";
					args.Context.AddString("Portal diagnostics " + (PortalPreviewPlugin.Verbose ? "enabled" : "disabled") + " for this session.");
					return;
				case "lighting":
				{
					if ((Object)(object)Player.m_localPlayer == (Object)null)
					{
						args.Context.AddString("Enter a world first.");
						return;
					}
					string text3 = "Lighting computed/live here: " + DestinationLighting.CompareWithLive(((Component)Player.m_localPlayer).transform.position);
					args.Context.AddString(text3);
					PortalPreviewPlugin.Log.LogInfo((object)("[portal preview] " + text3));
					return;
				}
				case "cleanup":
					if ((Object)(object)ZNet.instance == (Object)null)
					{
						args.Context.AddString("Enter a world first.");
						return;
					}
					PortalCleanup.Start(ArchiveDirectory);
					_status = "cleanup of stale saved scenes started; 'frozenportal status' shows the result";
					break;
				case "mode":
					if (args.Length != 3 || (args[2] != "always" && args[2] != "approach"))
					{
						args.Context.AddString("Usage: frozenportal mode always|approach");
						return;
					}
					PortalPreviewPlugin.Cfg.FrozenPortalAlwaysVisible.Value = args[2] == "always";
					break;
				case "profile":
				{
					if (args.Length != 3 || !Enum.TryParse<HardwareProfileSetting>(args[2], ignoreCase: true, out var result2))
					{
						args.Context.AddString("Usage: frozenportal profile auto|low|medium|high|ultra|custom");
						return;
					}
					PortalPreviewPlugin.Cfg.HardwareProfile.Value = result2;
					PreviewConfig cfg = PortalPreviewPlugin.Cfg;
					if (cfg.FrozenPortalResolution.Value > 0 || cfg.FrozenPortalMaxVisible.Value > 0 || cfg.FrozenPortalMaxResident.Value > 0)
					{
						cfg.FrozenPortalResolution.Value = 0;
						cfg.FrozenPortalMaxVisible.Value = 0;
						cfg.FrozenPortalMaxResident.Value = 0;
						args.Context.AddString("Resolution, Max visible portals and Max resident snapshots follow the profile again (set to 0).");
					}
					break;
				}
				case "count":
				case "spiral":
				case "radius":
				case "memory":
				{
					if (args.Length != 3 || !float.TryParse(args[2].Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || float.IsNaN(result) || float.IsInfinity(result))
					{
						args.Context.AddString("Usage: frozenportal " + text + " <number>");
						return;
					}
					if (text == "count")
					{
						PortalPreviewPlugin.Cfg.FrozenPortalMaxVisible.Value = ((!(result <= 0f)) ? Mathf.Clamp(Mathf.RoundToInt(result), 1, 16) : 0);
					}
					if (text == "memory")
					{
						PortalPreviewPlugin.Cfg.FrozenPortalMaxResident.Value = ((!(result <= 0f)) ? Mathf.Clamp(Mathf.RoundToInt(result), 2, 128) : 0);
					}
					if (text == "radius")
					{
						PortalPreviewPlugin.Cfg.FrozenPortalApproachRadius.Value = Mathf.Clamp(result, 1f, 100f);
					}
					if (text == "spiral")
					{
						PortalPreviewPlugin.Cfg.FrozenPortalRevealSeconds.Value = Mathf.Clamp(result, 0f, 5f);
					}
					break;
				}
				case "recapture":
				{
					if (args.Length != 3 || !float.TryParse(args[2].Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result3) || result3 < 0.02f || result3 > 1f)
					{
						args.Context.AddString("Usage: frozenportal recapture <metres, 0.02-1; 0.3 by default>");
						return;
					}
					PortalView.EyeRecaptureDistance = result3;
					_status = $"eye recapture every {result3:F2} m (diagnostic, not saved)";
					break;
				}
				case "pair":
					if (args.Length != 3 || (args[2] != "on" && args[2] != "off"))
					{
						args.Context.AddString("Usage: frozenportal pair on|off");
						return;
					}
					PortalView.EyePrediction = args[2] == "on";
					_status = "eye capture pair " + args[2] + " (diagnostic, not saved)";
					break;
				case "buffers":
					if (args.Length != 3 || (args[2] != "on" && args[2] != "off"))
					{
						args.Context.AddString("Usage: frozenportal buffers on|off");
						return;
					}
					PortalView.WriteVirtualBuffers = args[2] == "on";
					_status = "portal depth and motion vectors " + (PortalView.WriteVirtualBuffers ? "written" : "not written (diagnostic)");
					break;
				default:
					args.Context.AddString("Usage: frozenportal on|off|status|clear|refresh|backdropcheck|cleanup|lighting|buffers on|off|pair on|off|recapture <m>; profile auto|low|medium|high|ultra|custom; mode always|approach; count|radius|spiral|memory <number> (count and memory 0: the profile's)");
					return;
				case "status":
					break;
				}
				args.Context.AddString($"Frozen portals (scene-v20): {_status}; {CountSnapshots()}/{MaxSnapshots} resident snapshots; " + string.Format("up to {0} views; {1}. ", MaxVisible, PortalPreviewPlugin.Cfg.FrozenPortalAlwaysVisible.Value ? "always" : ("approach " + PortalPreviewPlugin.Cfg.FrozenPortalApproachRadius.Value + " m")) + $"Snapshot updates: {PortalPreviewPlugin.Cfg.SnapshotUpdates.Value}; " + "Scene, grass, particles, clouds and world tree frozen; sun and fog " + (PortalPreviewPlugin.Cfg.FollowTimeOfDay.Value ? "follow the time of day" : "frozen") + "; disk snapshots retained.");
				args.Context.AddString(HardwareProfile.Describe());
				if (PortalCleanup.LastReport != null)
				{
					args.Context.AddString("Last cleanup: " + PortalCleanup.LastReport);
				}
				string text4 = DescribeNearest(Utils.GetMainCamera());
				args.Context.AddString(text4);
				PortalPreviewPlugin.Diag("[portal preview] status: " + text4);
			}

			internal int <CaptureOne>b__101_0(Endpoint a, Endpoint b)
			{
				//IL_000a: Unknown result type (might be due to invalid IL or missing references)
				//IL_001a: Unknown result type (might be due to invalid IL or missing references)
				//IL_001f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				//IL_0039: Unknown result type (might be due to invalid IL or missing references)
				//IL_0049: Unknown result type (might be due to invalid IL or missing references)
				//IL_004e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0053: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val = ((Component)Player.m_localPlayer).transform.position - ((Component)a.Portal).transform.position;
				float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
				val = ((Component)Player.m_localPlayer).transform.position - ((Component)b.Portal).transform.position;
				return sqrMagnitude.CompareTo(((Vector3)(ref val)).sqrMagnitude);
			}

			internal int <.cctor>b__155_0(Endpoint a, Endpoint b)
			{
				return DistanceSquaredToPlayer(a).CompareTo(DistanceSquaredToPlayer(b));
			}
		}

		private static readonly Dictionary<ZDOID, Endpoint> Endpoints = new Dictionary<ZDOID, Endpoint>();

		private static readonly List<ZDOID> Stale = new List<ZDOID>();

		private static readonly Dictionary<ZDOID, PortalView> Views = new Dictionary<ZDOID, PortalView>();

		private static readonly HashSet<ZDOID> VisibleTargets = new HashSet<ZDOID>();

		private static readonly List<ArchivedScene> ArchivedScenes = new List<ArchivedScene>();

		private static string _indexedArchiveDirectory;

		private static ZNet _session;

		private static int _layer = -1;

		private static bool _failed;

		private static float _maintenanceAt;

		private static string _status = "off";

		private static string _lastViewDiagnostic;

		private static float _viewDiagnosticAt;

		private static float _statusAt;

		private static bool _renderAllowed;

		private static int _renderedFrame = -1;

		private const float ViewRetentionSeconds = 20f;

		private static readonly List<Endpoint> CandidateBuffer = new List<Endpoint>();

		private static readonly List<ViewCandidate> Renderable = new List<ViewCandidate>();

		private static readonly Plane[] FrustumPlanes = (Plane[])(object)new Plane[6];

		private static readonly FieldInfo LoadedHeightmaps = AccessTools.Field(typeof(Heightmap), "s_heightmaps");

		private static float _resumePreviewAt;

		private static IEnumerator<PortalSnapshot> _loadSteps;

		private static Endpoint _loading;

		private static long _loadStarted;

		private static double _loadWorkMs;

		private static ZDOID _arrival;

		private static ZDOID _teleportCapture;

		private static readonly TeleportCaptureGate TeleportGate = new TeleportCaptureGate();

		private static Vector3 _departurePlayerPosition;

		private static readonly Dictionary<string, string> ArchiveDirectories = new Dictionary<string, string>();

		private static bool _wasTeleporting;

		private static double _frameCostMaxMs;

		private static readonly Dictionary<string, float> NextStallReportAt = new Dictionary<string, float>();

		private static float _nextSlowFrameLogAt;

		private static int _frameCostSlow;

		private static double _frameCostMs;

		private static int _frameCostFrames;

		private static float _frameCostAt;

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

		private static int _refreshCursor;

		private const float EyeCaptureRetentionSeconds = 2f;

		private static readonly List<PortalSnapshot> RenderedScenes = new List<PortalSnapshot>();

		private static readonly List<PortalViewBudget.Entry<Endpoint>> ViewBudget = new List<PortalViewBudget.Entry<Endpoint>>();

		private static readonly RaycastHit[] OcclusionHits = (RaycastHit[])(object)new RaycastHit[8];

		private static int _occlusionMask = -1;

		private static IEnumerator<PortalCapture> _captureSteps;

		private static PortalSnapshot _capturing;

		private const float RelightSpacing = 10f;

		private static bool _lightingFailed;

		internal const float RecentCaptureSeconds = 120f;

		private static (bool Refresh, CaptureWhen When, bool FirstLink) _departurePending;

		internal const float ArrivalHoldSeconds = 4f;

		private const float ArrivalSettleSeconds = 0.5f;

		private static float _arrivalHoldUntil;

		private static bool _arrivalHoldDone;

		private static int _arrivalInstances = -1;

		private static float _arrivalInstancesSince;

		private static string _viewDiagnostics;

		private static float _diagnosticsUntil;

		private static ZDOID _instantArrival;

		private static ZDOID _arrivalPreparationFailed;

		private static float _instantArrivalUntil;

		private const float InstantArrivalSeconds = 60f;

		private const float InstantArrivalGrace = 3f;

		private static string ArchiveDirectory => ArchiveDirectoryFor(SnapshotRoot, ZNet.instance.GetWorldUID().ToString());

		private static int MaxVisible => Mathf.Clamp(HardwareProfile.MaxVisiblePortals, 1, 16);

		private static int MaxSnapshots => Mathf.Max(MaxVisible + 1, Mathf.Clamp(HardwareProfile.MaxResidentSnapshots, 2, 128));

		internal static string SnapshotRoot => Path.Combine(Paths.BepInExRootPath, "cache", "PortalPreview", "PortalSnapshots");

		internal static string LegacySnapshotRoot => Path.Combine(Paths.BepInExRootPath, "cache", "FastLoading", "PortalSnapshots");

		public static bool Enabled
		{
			get
			{
				if (PortalPreviewPlugin.Cfg != null && PortalPreviewPlugin.Cfg.Enabled.Value)
				{
					return !_failed;
				}
				return false;
			}
		}

		private static float CaptureIdleSeconds => HardwareProfile.CaptureIdleSeconds;

		private static float FingerprintRadius => Mathf.Clamp(PortalPreviewPlugin.Cfg.FrozenPortalRadius.Value, 32f, 128f);

		private static bool ArrivalOnly
		{
			get
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0005: Unknown result type (might be due to invalid IL or missing references)
				if (_instantArrival != ZDOID.None)
				{
					return Time.realtimeSinceStartup < _resumePreviewAt;
				}
				return false;
			}
		}

		internal static string ArchiveDirectoryFor(string root, string world)
		{
			string key = root + "|" + world;
			if (ArchiveDirectories.TryGetValue(key, out var value))
			{
				return value;
			}
			string text = Path.Combine(root, world, PortalSnapshot.RevisionFolder);
			string text2 = Path.Combine(root, world, PortalSnapshot.GameRevision);
			if (!Directory.Exists(text) && Directory.Exists(text2))
			{
				try
				{
					Directory.Move(text2, text);
				}
				catch (Exception ex)
				{
					PortalPreviewPlugin.Log.LogError((object)("[portal preview] Could not shorten the snapshot folder " + text2 + " (" + ex.Message + "); it stays in use."));
					text = text2;
				}
			}
			ArchiveDirectories[key] = text;
			return text;
		}

		internal static void AdoptLegacySnapshots()
		{
			try
			{
				if (Directory.Exists(LegacySnapshotRoot) && !Directory.Exists(SnapshotRoot))
				{
					Directory.CreateDirectory(Path.GetDirectoryName(SnapshotRoot));
					Directory.Move(LegacySnapshotRoot, SnapshotRoot);
					PortalPreviewPlugin.Diag("[portal preview] Snapshots saved by FastLoading moved to " + SnapshotRoot);
				}
			}
			catch (Exception ex)
			{
				PortalPreviewPlugin.Log.LogError((object)("[portal preview] Could not move FastLoading's snapshots (" + ex.Message + "); portals are captured again."));
			}
		}

		public static void Initialize()
		{
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Expected O, but got Unknown
			PortalPreviewPlugin.Diag(HardwareProfile.Describe());
			PortalView.BindWaterReflection(null);
			PortalPreviewPlugin.Cfg.FrozenPortalRadius.SettingChanged += ResetCaptureRadii;
			PortalPreviewPlugin.Cfg.FrozenPortalMaxRenderers.SettingChanged += ResetCaptureRadii;
			PortalPreviewPlugin.Cfg.FrozenPortalMaxUniqueVertices.SettingChanged += ResetCaptureRadii;
			object obj = <>c.<>9__49_0;
			if (obj == null)
			{
				ConsoleEvent val = delegate(ConsoleEventArgs args)
				{
					//IL_046d: Unknown result type (might be due to invalid IL or missing references)
					string text = ((args.Length > 1) ? args[1].ToLowerInvariant() : "status");
					switch (text)
					{
					case "on":
						Clear();
						PortalPreviewPlugin.Cfg.Enabled.Value = true;
						_status = "enabled; waiting for a loaded wooden or stone portal";
						break;
					case "off":
						PortalPreviewPlugin.Cfg.Enabled.Value = false;
						Clear();
						break;
					case "clear":
						Clear();
						_status = "RAM cache cleared; saved snapshots retained on disk";
						break;
					case "backdropcheck":
					{
						if (RefreshNearby() == 0)
						{
							args.Context.AddString("No connected portal within 24 m to capture.");
							return;
						}
						string text2 = Path.Combine(Paths.BepInExRootPath, "cache", "PortalPreview", "diagnostics", "backdrop-" + DateTime.Now.ToString("yyyyMMdd-HHmmss"));
						Directory.CreateDirectory(text2);
						PortalSnapshot.BackdropDiagnostics = text2;
						_viewDiagnostics = text2;
						_diagnosticsUntil = Time.realtimeSinceStartup + 60f;
						args.Context.AddString("The next capture of a nearby portal writes a panorama and view comparison to " + text2);
						break;
					}
					case "refresh":
						RefreshNearby();
						break;
					case "log":
						if (args.Length != 3 || (args[2] != "on" && args[2] != "off"))
						{
							args.Context.AddString("Usage: frozenportal log on|off");
						}
						else
						{
							PortalPreviewPlugin.Verbose = args[2] == "on";
							args.Context.AddString("Portal diagnostics " + (PortalPreviewPlugin.Verbose ? "enabled" : "disabled") + " for this session.");
						}
						return;
					case "lighting":
						if ((Object)(object)Player.m_localPlayer == (Object)null)
						{
							args.Context.AddString("Enter a world first.");
						}
						else
						{
							string text3 = "Lighting computed/live here: " + DestinationLighting.CompareWithLive(((Component)Player.m_localPlayer).transform.position);
							args.Context.AddString(text3);
							PortalPreviewPlugin.Log.LogInfo((object)("[portal preview] " + text3));
						}
						return;
					case "cleanup":
						if ((Object)(object)ZNet.instance == (Object)null)
						{
							args.Context.AddString("Enter a world first.");
							return;
						}
						PortalCleanup.Start(ArchiveDirectory);
						_status = "cleanup of stale saved scenes started; 'frozenportal status' shows the result";
						break;
					case "mode":
						if (args.Length != 3 || (args[2] != "always" && args[2] != "approach"))
						{
							args.Context.AddString("Usage: frozenportal mode always|approach");
							return;
						}
						PortalPreviewPlugin.Cfg.FrozenPortalAlwaysVisible.Value = args[2] == "always";
						break;
					case "profile":
					{
						if (args.Length != 3 || !Enum.TryParse<HardwareProfileSetting>(args[2], ignoreCase: true, out var result2))
						{
							args.Context.AddString("Usage: frozenportal profile auto|low|medium|high|ultra|custom");
							return;
						}
						PortalPreviewPlugin.Cfg.HardwareProfile.Value = result2;
						PreviewConfig cfg = PortalPreviewPlugin.Cfg;
						if (cfg.FrozenPortalResolution.Value > 0 || cfg.FrozenPortalMaxVisible.Value > 0 || cfg.FrozenPortalMaxResident.Value > 0)
						{
							cfg.FrozenPortalResolution.Value = 0;
							cfg.FrozenPortalMaxVisible.Value = 0;
							cfg.FrozenPortalMaxResident.Value = 0;
							args.Context.AddString("Resolution, Max visible portals and Max resident snapshots follow the profile again (set to 0).");
						}
						break;
					}
					case "count":
					case "spiral":
					case "radius":
					case "memory":
					{
						if (args.Length != 3 || !float.TryParse(args[2].Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || float.IsNaN(result) || float.IsInfinity(result))
						{
							args.Context.AddString("Usage: frozenportal " + text + " <number>");
							return;
						}
						if (text == "count")
						{
							PortalPreviewPlugin.Cfg.FrozenPortalMaxVisible.Value = ((!(result <= 0f)) ? Mathf.Clamp(Mathf.RoundToInt(result), 1, 16) : 0);
						}
						if (text == "memory")
						{
							PortalPreviewPlugin.Cfg.FrozenPortalMaxResident.Value = ((!(result <= 0f)) ? Mathf.Clamp(Mathf.RoundToInt(result), 2, 128) : 0);
						}
						if (text == "radius")
						{
							PortalPreviewPlugin.Cfg.FrozenPortalApproachRadius.Value = Mathf.Clamp(result, 1f, 100f);
						}
						if (text == "spiral")
						{
							PortalPreviewPlugin.Cfg.FrozenPortalRevealSeconds.Value = Mathf.Clamp(result, 0f, 5f);
						}
						break;
					}
					case "recapture":
					{
						if (args.Length != 3 || !float.TryParse(args[2].Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result3) || result3 < 0.02f || result3 > 1f)
						{
							args.Context.AddString("Usage: frozenportal recapture <metres, 0.02-1; 0.3 by default>");
							return;
						}
						PortalView.EyeRecaptureDistance = result3;
						_status = $"eye recapture every {result3:F2} m (diagnostic, not saved)";
						break;
					}
					case "pair":
						if (args.Length != 3 || (args[2] != "on" && args[2] != "off"))
						{
							args.Context.AddString("Usage: frozenportal pair on|off");
							return;
						}
						PortalView.EyePrediction = args[2] == "on";
						_status = "eye capture pair " + args[2] + " (diagnostic, not saved)";
						break;
					case "buffers":
						if (args.Length != 3 || (args[2] != "on" && args[2] != "off"))
						{
							args.Context.AddString("Usage: frozenportal buffers on|off");
							return;
						}
						PortalView.WriteVirtualBuffers = args[2] == "on";
						_status = "portal depth and motion vectors " + (PortalView.WriteVirtualBuffers ? "written" : "not written (diagnostic)");
						break;
					default:
						args.Context.AddString("Usage: frozenportal on|off|status|clear|refresh|backdropcheck|cleanup|lighting|buffers on|off|pair on|off|recapture <m>; profile auto|low|medium|high|ultra|custom; mode always|approach; count|radius|spiral|memory <number> (count and memory 0: the profile's)");
						return;
					case "status":
						break;
					}
					args.Context.AddString($"Frozen portals (scene-v20): {_status}; {CountSnapshots()}/{MaxSnapshots} resident snapshots; " + string.Format("up to {0} views; {1}. ", MaxVisible, PortalPreviewPlugin.Cfg.FrozenPortalAlwaysVisible.Value ? "always" : ("approach " + PortalPreviewPlugin.Cfg.FrozenPortalApproachRadius.Value + " m")) + $"Snapshot updates: {PortalPreviewPlugin.Cfg.SnapshotUpdates.Value}; " + "Scene, grass, particles, clouds and world tree frozen; sun and fog " + (PortalPreviewPlugin.Cfg.FollowTimeOfDay.Value ? "follow the time of day" : "frozen") + "; disk snapshots retained.");
					args.Context.AddString(HardwareProfile.Describe());
					if (PortalCleanup.LastReport != null)
					{
						args.Context.AddString("Last cleanup: " + PortalCleanup.LastReport);
					}
					string text4 = DescribeNearest(Utils.GetMainCamera());
					args.Context.AddString(text4);
					PortalPreviewPlugin.Diag("[portal preview] status: " + text4);
				};
				<>c.<>9__49_0 = val;
				obj = (object)val;
			}
			new ConsoleCommand("frozenportal", "Frozen 3D portals: on, off, status, clear (RAM), refresh (nearby)", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		}

		public static void Observe(TeleportWorld portal, bool newlyLinked = false)
		{
			//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_0070: 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_0090: 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_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			if (!Enabled || (Object)(object)portal == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null || PortalShape.For(((Component)portal).transform) == null)
			{
				return;
			}
			if ((Object)(object)_session != (Object)(object)ZNet.instance)
			{
				Clear();
				_session = ZNet.instance;
			}
			ZNetView component = ((Component)portal).GetComponent<ZNetView>();
			ZDO val = ((component != null) ? component.GetZDO() : null);
			if (val == null)
			{
				return;
			}
			ZDOID connectionZDOID = val.GetConnectionZDOID((ConnectionType)1);
			if (!Endpoints.TryGetValue(val.m_uid, out var value))
			{
				value = new Endpoint
				{
					Id = val.m_uid,
					Target = connectionZDOID,
					StableAt = Time.realtimeSinceStartup + 3f
				};
				Endpoints.Add(val.m_uid, value);
			}
			bool flag = value.Observed && value.Target != connectionZDOID && connectionZDOID != ZDOID.None;
			value.Observed = true;
			if (value.Target != connectionZDOID)
			{
				value.Target = connectionZDOID;
				value.Reported = false;
			}
			value.Portal = portal;
			if (newlyLinked || flag)
			{
				RequestFirstLinkCapture(value, "portal connection established", CaptureWhen.WhenShown);
			}
			else if (connectionZDOID != ZDOID.None && !value.Refresh && value.Snapshot == null)
			{
				Vector3 val2 = ((Component)portal).transform.position - ((Component)Player.m_localPlayer).transform.position;
				if (((Vector3)(ref val2)).sqrMagnitude <= 64f)
				{
					RequestFirstLinkCapture(value, "connected endpoint first seen nearby", CaptureWhen.AtTeleport);
				}
			}
		}

		private static Endpoint Unobserved(ZDOID id)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: 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_0059: Unknown result type (might be due to invalid IL or missing references)
			Endpoint obj = new Endpoint
			{
				Id = id
			};
			ZDOMan instance = ZDOMan.instance;
			ZDOID? obj2;
			if (instance == null)
			{
				obj2 = null;
			}
			else
			{
				ZDO zDO = instance.GetZDO(id);
				obj2 = ((zDO != null) ? new ZDOID?(zDO.GetConnectionZDOID((ConnectionType)1)) : ((ZDOID?)null));
			}
			obj.Target = (ZDOID)(((??)obj2) ?? ZDOID.None);
			return obj;
		}

		private static void ResetCaptureRadii(object sender, EventArgs args)
		{
			foreach (Endpoint value in Endpoints.Values)
			{
				value.CaptureRadius = 0f;
			}
		}

		internal static void TagChanged(TeleportWorld portal)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			Observe(portal);
			object obj;
			if (!((Object)(object)portal != (Object)null))
			{
				obj = null;
			}
			else
			{
				ZNetView component = ((Component)portal).GetComponent<ZNetView>();
				obj = ((component != null) ? component.GetZDO() : null);
			}
			ZDO val = (ZDO)obj;
			if (val != null && Endpoints.TryGetValue(val.m_uid, out var value) && value.Target != ZDOID.None)
			{
				RequestFirstLinkCapture(value, "portal tag assigned", CaptureWhen.WhenShown);
			}
		}

		private static bool NeedsFirstCapture(Endpoint endpoint)
		{
			string path = PortalSnapshot.ArchivePath(ArchiveDirectory, ((object)Unsafe.As<ZDOID, ZDOID>(ref endpoint.Id)/*cast due to .constrained prefix*/).ToString());
			bool flag = endpoint.Snapshot != null && !endpoint.Snapshot.IsLegacyArchive;
			if (!flag && File.Exists(path))
			{
				flag = PortalSnapshot.IsCurrentArchive(path, ((object)Unsafe.As<ZDOID, ZDOID>(ref endpoint.Id)/*cast due to .constrained prefix*/).ToString());
			}
			if (flag)
			{
				return endpoint.DiskLoadFailed;
			}
			return true;
		}

		private static void RequestFirstLinkCapture(Endpoint endpoint, string reason, CaptureWhen when)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			if (NeedsFirstCapture(endpoint))
			{
				Schedule(endpoint, when);
				endpoint.FirstLinkCapture = true;
				endpoint.RetryAt = 0f;
				endpoint.StableAt = Mathf.Max(endpoint.StableAt, Time.realtimeSinceStartup + 0.5f);
				_status = $"first-link capture scheduled for {endpoint.Id}: {reason}";
			}
		}

		private static void Defer(Endpoint endpoint)
		{
			Schedule(endpoint, CaptureWhen.AtTeleport);
		}

		private static void Schedule(Endpoint endpoint, CaptureWhen when)
		{
			endpoint.When = ((endpoint.Refresh && endpoint.When < when) ? endpoint.When : when);
			endpoint.Refresh = true;
		}

		private static bool ShownNearby(Endpoint endpoint)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			foreach (Endpoint value in Endpoints.Values)
			{
				if (value != endpoint && (Object)(object)value.Portal != (Object)null && value.Target == endpoint.Id)
				{
					return true;
				}
			}
			return false;
		}

		private static void Invalidate(Endpoint endpoint)
		{
			endpoint.Snapshot?.Dispose();
			endpoint.Snapshot = null;
			endpoint.SnapshotVerified = false;
			endpoint.RetryAt = 0f;
			endpoint.Reported = false;
			endpoint.LastError = null;
		}

		private static int CountSnapshots()
		{
			int num = 0;
			foreach (Endpoint value in Endpoints.Values)
			{
				if (value.Snapshot != null)
				{
					num++;
				}
			}
			return num;
		}

		public static void Tick()
		{
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			//IL_0229: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_020e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: 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_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			if (!Enabled || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ZNet.instance == (Object)null)
			{
				if (Endpoints.Count > 0 || Views.Count > 0)
				{
					Clear();
				}
				_wasTeleporting = false;
				return;
			}
			if ((Object)(object)_session != (Object)(object)ZNet.instance)
			{
				Clear();
				_session = ZNet.instance;
				return;
			}
			_renderAllowed = false;
			if (_diagnosticsUntil > 0f && Time.realtimeSinceStartup > _diagnosticsUntil)
			{
				ForgetDiagnostics();
			}
			DetachViews();
			bool flag = ((Character)Player.m_localPlayer).IsTeleporting();
			bool flag2 = _wasTeleporting && !flag;
			_wasTeleporting = flag;
			if (flag)
			{
				DetachViews();
				CaptureDuringTeleport();
				CaptureArrivalDuringTeleport();
				PrepareArrivalView();
				return;
			}
			if (flag2)
			{
				long timestamp = Stopwatch.GetTimestamp();
				if (_arrival != ZDOID.None && Endpoints.TryGetValue(_arrival, out var value))
				{
					RequestFirstLinkCapture(value, "first arrival at linked exit", CaptureWhen.AtTeleport);
				}
				_resumePreviewAt = Time.realtimeSinceStartup + Mathf.Clamp(PortalPreviewPlugin.Cfg.FrozenPortalPostTeleportDelay.Value, 0f, 10f);
				if (_instantArrival != ZDOID.None)
				{
					_instantArrivalUntil = _resumePreviewAt + 3f;
				}
				DetachViews();
				_arrival = ZDOID.None;
				if (_teleportCapture != ZDOID.None)
				{
					PortalPreviewPlugin.DiagWarning($"[portal preview] Departure {_teleportCapture} could not be captured before teleport loading finished; the previous disk snapshot was retained.");
					if (Endpoints.TryGetValue(_teleportCapture, out var value2))
					{
						RestoreDeparturePending(value2);
					}
					_teleportCapture = ZDOID.None;
					TeleportGate.Reset();
				}
				ReportStall("arrival bookkeeping", timestamp);
			}
			if (_instantArrival != ZDOID.None && (Time.realtimeSinceStartup > _instantArrivalUntil || !Endpoints.TryGetValue(_instantArrival, out var value3) || ((Object)(object)value3.Portal != (Object)null && !ApproachActive(value3, wasActive: false))))
			{
				_instantArrival = ZDOID.None;
			}
			if (Time.realtimeSinceStartup < _resumePreviewAt)
			{
				DetachViews();
				_renderAllowed = _instantArrival != ZDOID.None;
				return;
			}
			try
			{
				long timestamp2 = Stopwatch.GetTimestamp();
				Endpoint loading = _loading;
				StepLoad();
				ReportStall("archive load step", timestamp2, (loading != null) ? $"loading {loading.Id}" : null);
				Camera mainCamera = Utils.GetMainCamera();
				if ((Object)(object)mainCamera == (Object)null)
				{
					DetachViews();
					return;
				}
				if (Time.realtimeSinceStartup >= _maintenanceAt)
				{
					timestamp2 = Stopwatch.GetTimestamp();
					StallProfile.Start();
					_maintenanceAt = Time.realtimeSinceStartup + 1f;
					PortalCleanup.Tick(ArchiveDirectory, _loadSteps != null);
					StallProfile.Mark("cleanup");
					Maintain();
					LoadVisible(mainCamera);
					StallProfile.Mark("load visible");
					CaptureOne();
					StallProfile.Mark("capture one");
					while (CountSnapshots() > MaxSnapshots && MakeRoom(new Endpoint()))
					{
					}
					StallProfile.Mark("evict");
					ReportStall("maintenance/capture/save", timestamp2, StallProfile.Describe());
				}
				_renderAllowed = true;
			}
			catch (Exception exception)
			{
				Fail(exception);
			}
		}

		internal static void RenderFrame()
		{
			if (!_renderAllowed || !Enabled || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				return;
			}
			Camera mainCamera = Utils.GetMainCamera();
			if ((Object)(object)mainCamera == (Object)null)
			{
				return;
			}
			long timestamp = Stopwatch.GetTimestamp();
			PortalView.Reconstruct = PortalPreviewPlugin.Cfg.FrozenPortalShaderReconstruction.Value && PortalShaders.Available;
			PortalView.FrameCaptures = (PortalView.FrameAllocations = 0);
			PortalView.FrameCaptureTicks = (PortalView.FrameAllocationTicks = 0L);
			long num = 0L;
			try
			{
				foreach (PortalView value in Views.Values)
				{
					value.Detach();
				}
				long timestamp2 = Stopwatch.GetTimestamp();
				bool flag = false;
				foreach (ViewCandidate item in Renderable)
				{
					if (item.View.Active && item.Snapshot.ImageCapture != null)
					{
						flag = true;
						break;
					}
				}
				bool flag2 = _renderedFrame != Time.frameCount && !ArrivalOnly && (!flag || (Time.frameCount & 1) == 0) && StepImageCapture();
				num = Stopwatch.GetTimestamp() - timestamp2;
				RenderViews(mainCamera, !flag2 && _renderedFrame != Time.frameCount);
				_renderedFrame = Time.frameCount;
			}
			catch (Exception exception)
			{
				Fail(exception);
				return;
			}
			double num2 = (double)(Stopwatch.GetTimestamp() - timestamp) * 1000.0 / (double)Stopwatch.Frequency;
			double num3 = 1000.0 / (double)Stopwatch.Frequency;
			ReportStall("preview render", timestamp, $"{PortalView.FrameCaptures} eye captures {(double)PortalView.FrameCaptureTicks * num3:F0} ms, " + $"opening-centre capture step {(double)num * num3:F0} ms, {Views.Count} views");
			_frameCostMs += num2;
			_frameCostMaxMs = Math.Max(_frameCostMaxMs, num2);
			if (num2 >= 8.0 && Time.realtimeSinceStartup >= _nextSlowFrameLogAt)
			{
				_nextSlowFrameLogAt = Time.realtimeSinceStartup + 1f;
				double num4 = 1000.0 / (double)Stopwatch.Frequency;
				double num5 = (double)PortalView.FrameCaptureTicks * num4;
				double num6 = (double)num * num4;
				PortalPreviewPlugin.Diag($"[portal preview] Slow frame {num2:F1} ms: {PortalView.FrameCaptures} eye captures " + $"{num5:F1} ms (of which {PortalView.FrameAllocations} texture allocations {(double)PortalView.FrameAllocationTicks * num4:F1} ms), " + $"opening-centre capture step {num6:F1} ms, rest {num2 - num5 - num6:F1} ms; {Views.Count} views");
			}
			if (num2 >= 8.0)
			{
				_frameCostSlow++;
			}
			_frameCostFrames++;
			if (_frameCostAt <= 0f)
			{
				_frameCostAt = Time.realtimeSinceStartup;
			}
			if (!(Time.realtimeSinceStartup - _frameCostAt < 5f))
			{
				PortalPreviewPlugin.Diag($"[portal preview] Frame cost: mean {_frameCostMs / (double)_frameCostFrames:F2} ms, " + $"max {_frameCostMaxMs:F1} ms, {_frameCostSlow} frames over 8 ms, over {_frameCostFrames} frames with {Views.Count} views; " + $"{PortalView.EyeTextureAllocations} eye capture allocations; eye within {PortalView.EyeCoverageMax:F2} m of a capture, " + $"{PortalView.EyeBeyondReach} draws beyond reach (recapture {PortalView.EyeRecaptureDistance:F2} m, " + string.Format("pair {0}, quality {1})", PortalView.EyePrediction ? "on" : "off", PortalView.Quality));
				PortalView.EyeCoverageMax = 0f;
				PortalView.EyeBeyondReach = 0;
				_frameCostAt = Time.realtimeSinceStartup;
				_frameCostMs = (_frameCostMaxMs = 0.0);
				_frameCostFrames = (_frameCostSlow = 0);
				PortalView.EyeTextureAllocations = 0;
			}
		}

		private static void ReportStall(string phase, long started, string detail = null)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			double num = (double)(Stopwatch.GetTimestamp() - started) * 1000.0 / (double)Stopwatch.Frequency;
			if (!(num < 50.0) && (!NextStallReportAt.TryGetValue(phase, out var value) || !(Time.realtimeSinceStartup < value)))
			{
				NextStallReportAt[phase] = Time.realtimeSinceStartup + 10f;
				PortalPreviewPlugin.Log.LogWarning((object)($"[portal preview] {DateTime.Now:HH:mm:ss} Slow {phase}: {num:F0} ms " + $"({SystemInfo.graphicsDeviceType})" + ((detail != null) ? (": " + detail) : "") + "; use 'frozenportal log on' for detailed timings."));
			}
		}

		private static void Fail(Exception exception)
		{
			Clear();
			_failed = true;
			_status = "disabled after a render/capture error; see log; frozenportal on retries";
			PortalPreviewPlugin.Log.LogError((object)("[portal preview] " + exception));
		}

		private static float DistanceSquaredToPlayer(Endpoint endpoint)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = ((Component)endpoint.Portal).transform.position - ((Component)Player.m_localPlayer).transform.position;
			return ((Vector3)(ref val)).sqrMagnitude;
		}

		private static bool ApproachActive(Endpoint endpoint, bool wasActive)
		{
			return PortalVisibility.Active(PortalPreviewPlugin.Cfg.FrozenPortalAlwaysVisible.Value, DistanceSquaredToPlayer(endpoint), Mathf.Clamp(PortalPreviewPlugin.Cfg.FrozenPortalApproachRadius.Value, 1f, 100f), wasActive);
		}

		private static List<Endpoint> Candidates(Camera viewer, bool includeClosing = false)
		{
			//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_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: 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_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: 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_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			List<Endpoint> candidateBuffer = CandidateBuffer;
			candidateBuffer.Clear();
			GeometryUtility.CalculateFrustumPlanes(viewer, FrustumPlanes);
			foreach (Endpoint value2 in Endpoints.Values)
			{
				if (!((Object)(object)value2.Portal == (Object)null) && !(value2.Target == ZDOID.None))
				{
					Transform transform = ((Component)value2.Portal).transform;
					PortalShape portalShape = PortalShape.For(transform) ?? PortalShape.Wood;
					Vector3 val = transform.position + transform.rotation * portalShape.Center;
					Views.TryGetValue(value2.Id, out var value);
					if ((ApproachActive(value2, value?.Active ?? false) || (includeClosing && value != null && value.Progress > 0f)) && GeometryUtility.TestPlanesAABB(FrustumPlanes, new Bounds(val, Vector3.one * portalShape.Height)))
					{
						candidateBuffer.Add(value2);
					}
				}
			}
			candidateBuffer.Sort(NearestFirst);
			return candidateBuffer;
		}

		private static void LoadVisible(Camera viewer)
		{
			//IL_0037: 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_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: 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)
			VisibleTargets.Clear();
			List<Endpoint> list = Candidates(viewer);
			int maxVisible = MaxVisible;
			foreach (Endpoint item in list)
			{
				if (maxVisible-- <= 0)
				{
					break;
				}
				VisibleTargets.Add(item.Target);
			}
			foreach (Endpoint item2 in Candidates(viewer, includeClosing: true))
			{
				if (Views.TryGetValue(item2.Id, out var value) && value.Progress > 0f)
				{
					VisibleTargets.Add(item2.Target);
				}
			}
			foreach (Endpoint item3 in Candidates(viewer))
			{
				if (!VisibleTargets.Contains(item3.Target))
				{
					continue;
				}
				if (!Endpoints.TryGetValue(item3.Target, out var value2))
				{
					value2 = Unobserved(item3.Target);
					Endpoints.Add(value2.Id, value2);
				}
				value2.LastUsed = Time.realtimeSinceStartup;
				if (EntryLinkIsCurrent(item3, value2) && value2.Snapshot == null && Time.realtimeSinceStartup >= value2.RetryAt)
				{
					TryLoad(value2);
					if (_loading == value2)
					{
						break;
					}
				}
			}
		}

		private static void RenderViews(Camera viewer, bool allowRefresh)
		{
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_0365: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0489: Unknown result type (might be due to invalid IL or missing references)
			//IL_0602: Unknown result type (might be due to invalid IL or missing references)
			//IL_0613: Unknown result type (might be due to invalid IL or missing references)
			//IL_052a: Unknown result type (might be due to invalid IL or missing references)
			//IL_04fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0509: Unknown result type (might be due to invalid IL or missing references)
			//IL_0572: Unknown result type (might be due to invalid IL or missing references)
			//IL_0577: Unknown result type (might be due to invalid IL or missing references)
			//IL_0595: Unknown result type (might be due to invalid IL or missing references)
			//IL_059a: Unknown result type (might be due to invalid IL or missing references)
			foreach (PortalView value6 in Views.Values)
			{
				value6.Detach();
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			float duration = Mathf.Clamp(PortalPreviewPlugin.Cfg.FrozenPortalRevealSeconds.Value, 0f, 5f);
			int num = Mathf.Clamp(HardwareProfile.PortalResolution, 128, 2048);
			int num2 = 0;
			PortalView.EyeCaptureLimit = Mathf.Clamp(Mathf.RoundToInt(2f * (float)num), 1024, 4096);
			PortalView.Quality = HardwareProfile.PreviewQuality;
			string text = null;
			string text2 = null;
			bool value = PortalPreviewPlugin.Cfg.FrozenPortalOneSided.Value;
			ViewCandidate? viewCandidate = null;
			List<ViewCandidate> renderable = Renderable;
			renderable.Clear();
			ViewBudget.Clear();
			bool arrivalOnly = ArrivalOnly;
			foreach (Endpoint item in Candidates(viewer, includeClosing: true))
			{
				if ((!arrivalOnly || !(item.Id != _instantArrival)) && Endpoints.TryGetValue(item.Target, out var value2) && EntryLinkIsCurrent(item, value2) && SnapshotMatchesEndpoint(value2))
				{
					Views.TryGetValue(item.Id, out var value3);
					bool flag = ApproachActive(item, value3?.Active ?? false);
					PortalViewBudget.Entry<Endpoint> viewState = item.ViewState;
					viewState.Id = item;
					viewState.Distance = Mathf.Sqrt(DistanceSquaredToPlayer(item));
					viewState.Eligible = flag;
					viewState.Active = value3?.Active ?? false;
					viewState.Progress = value3?.Progress ?? 0f;
					viewState.Ready = !PortalView.Reconstruct || value2.Snapshot.ImageCapture != null;
					if (item.Id == _instantArrival && flag && viewState.Ready)
					{
						viewState.Progress = 1f;
						viewState.Active = true;
					}
					ViewBudget.Add(viewState);
				}
			}
			PortalViewBudget.Advance(ViewBudget, MaxVisible, Time.unscaledDeltaTime, duration);
			foreach (PortalViewBudget.Entry<Endpoint> item2 in ViewBudget)
			{
				Endpoint id = item2.Id;
				Views.TryGetValue(id.Id, out var value4);
				if (value4 != null || item2.Active)
				{
					int height = (PortalPreviewPlugin.Cfg.FrozenPortalAdaptiveResolution.Value ? PortalView.ScreenMatchedHeight(viewer, ((Component)id.Portal).transform, num, value4?.Resolution ?? 0) : num);
					if (value4 == null)
					{
						value4 = new PortalView(height);
						Views.Add(id.Id, value4);
					}
					else if (item2.Active)
					{
						value4.Resize(height);
					}
					value4.Active = item2.Active;
					value4.LastRequestedAt = realtimeSinceStartup;
					value4.Progress = item2.Progress;
					if (!(value4.Progress <= 0f) || value4.Active)
					{
						renderable.Add(new ViewCandidate
						{
							Endpoint = id,
							Snapshot = Endpoints[id.Target].Snapshot,
							View = value4
						});
					}
				}
			}
			AdvanceUnseenViews(realtimeSinceStartup, duration);
			RenderedScenes.Clear();
			foreach (ViewCandidate item3 in renderable)
			{
				RenderedScenes.Add(item3.Snapshot);
			}
			if (renderable.Count > 0)
			{
				PortalSnapshot.BeginSharedLightExclusion(_layer, RenderedScenes);
			}
			PortalVortex.BeginFrame();
			int num3 = ((renderable.Count == 0) ? (-1) : (_refreshCursor % renderable.Count));
			if (allowRefresh)
			{
				_refreshCursor = (_refreshCursor + 1) & 0x7FFFFFFF;
			}
			try
			{
				for (int i = 0; i < renderable.Count; i++)
				{
					ViewCandidate value5 = renderable[i];
					Endpoint endpoint = value5.Endpoint;
					PortalView view = value5.View;
					if (value && view.SeenFromBehind(viewer, ((Component)endpoint.Portal).transform))
					{
						text2 = $"{endpoint.Id} is seen from behind and the view is one-sided";
						continue;
					}
					bool refreshScene = allowRefresh && i == num3 && view.Active && (!view.HasImage || !Occluded(viewer, ((Component)endpoint.Portal).transform));
					if (!view.Render(viewer, ((Component)endpoint.Portal).transform, value5.Snapshot, refreshScene))
					{
						text = $"projection rejected for {endpoint.Id} using {endpoint.Target}";
						continue;
					}
					num2++;
					Endpoints[endpoint.Target].LastUsed = (endpoint.LastUsed = realtimeSinceStartup);
					if (!viewCandidate.HasValue)
					{
						viewCandidate = value5;
					}
					PortalVortex.Fade(((Component)endpoint.Portal).transform, view.Progress);
					if (endpoint.Id == _instantArrival && view.Progress >= 1f && !arrivalOnly)
					{
						_instantArrival = ZDOID.None;
					}
				}
			}
			finally
			{
				PortalSnapshot.EndSharedLightExclusion();
				PortalVortex.EndFrame();
			}
			RetireViews(realtimeSinceStartup);
			if (!(realtimeSinceStartup < _statusAt))
			{
				_statusAt = realtimeSinceStartup + 1f;
				if (viewCandidate.HasValue)
				{
					ViewCandidate valueOrDefault = viewCandidate.GetValueOrDefault();
					_status = $"showing {num2} portal views with frozen lighting: {valueOrDefault.Endpoint.Id} using {valueOrDefault.Endpoint.Target} " + $"({valueOrDefault.Snapshot.MeshCount} meshes, {valueOrDefault.Snapshot.TerrainCount} terrain, {valueOrDefault.Snapshot.GrassCount} grass, " + $"{valueOrDefault.Snapshot.ParticleCount} frozen particle systems, {valueOrDefault.Snapshot.WeatherParticleCount} weather systems)";
				}
				else
				{
					_status = text ?? text2 ?? DescribeNearest(viewer);
				}
				if (_status != _lastViewDiagnostic && realtimeSinceStartup >= _viewDiagnosticAt)
				{
					_lastViewDiagnostic = _status;
					_viewDiagnosticAt = realtimeSinceStartup + 5f;
					PortalPreviewPlugin.Diag("[portal preview] " + ((num2 > 0) ? "View drawn: " : "View not drawn: ") + _status);
				}
			}
		}

		private static void AdvanceUnseenViews(float now, float duration)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			foreach (KeyValuePair<ZDOID, PortalView> view in Views)
			{
				PortalView value = view.Value;
				if (value.LastRequestedAt != now)
				{
					value.Progress = PortalVisibility.Advance(active: value.Active = Endpoints.TryGetValue(view.Key, out var value2) && (Object)(object)value2.Portal != (Object)null && ApproachActive(value2, value.Active), progress: value.Progress, delta: Time.unscaledDeltaTime, seconds: duration);
				}
			}
		}

		private static void RetireViews(float now)
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			Stale.Clear();
			foreach (KeyValuePair<ZDOID, PortalView> view in Views)
			{
				if (now - view.Value.LastRequestedAt > 20f || (!view.Value.Active && view.Value.Progress <= 0f) || !Endpoints.ContainsKey(view.Key))
				{
					Stale.Add(view.Key);
				}
			}
			int num = Views.Count - Stale.Count - MaxVisible * 2;
			while (num-- > 0)
			{
				ZDOID val = ZDOID.None;
				float num2 = float.MaxValue;
				foreach (KeyValuePair<ZDOID, PortalView> view2 in Views)
				{
					if (view2.Value.LastRequestedAt < now && view2.Value.LastRequestedAt < num2 && !Stale.Contains(view2.Key))
					{
						val = view2.Key;
						num2 = view2.Value.LastRequestedAt;
					}
				}
				if (val == ZDOID.None)
				{
					break;
				}
				Stale.Add(val);
			}
			foreach (ZDOID item in Stale)
			{
				Views[item].Dispose();
				Views.Remove(item);
			}
			foreach (PortalView value in Views.Values)
			{
				if (now - value.LastRequestedAt > 2f && value.HoldsEyeCaptures)
				{
					value.ReleaseEyeCaptures();
				}
			}
		}

		private static bool Occluded(Camera viewer, Transform portal)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: 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_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: 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_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			if (_occlusionMask == -1)
			{
				_occlusionMask = LayerMask.GetMask(new string[6] { "Default", "static_solid", "Default_small", "piece", "terrain", "vehicle" });
			}
			Vector3 position = ((Component)viewer).transform.position;
			PortalShape portalShape = PortalShape.For(portal) ?? PortalShape.Wood;
			bool flag = Vector3.Dot(position - (portal.position + portal.rotation * portalShape.Center), portal.forward) < 0f;
			Vector3 val = (flag ? (-portal.forward) : portal.forward);
			Vector3 val2 = portal.position + portal.rotation * new Vector3(0f, portalShape.CenterHeight, portalShape.Plane(flag));
			for (int i = 0; i < 5; i++)
			{
				Vector3 val3 = (Vector3)(i switch
				{
					3 => Vector3.up * portalShape.Height * 0.3f, 
					2 => Vector3.left * portalShape.Width * 0.3f, 
					1 => Vector3.right * portalShape.Width * 0.3f, 
					0 => Vector3.zero, 
					_ => Vector3.down * portalShape.Height * 0.3f, 
				});
				Vector3 val4 = val2 + portal.rotation * val3 + val * 0.1f - position;
				float magnitude = ((Vector3)(ref val4)).magnitude;
				if (magnitude < 0.01f)
				{
					return false;
				}
				int num = Physics.RaycastNonAlloc(position, val4 / magnitude, OcclusionHits, magnitude, _occlusionMask, (QueryTriggerInteraction)1);
				bool flag2 = false;
				for (int j = 0; j < num; j++)
				{
					if (flag2)
					{
						break;
					}
					flag2 = !((Component)((RaycastHit)(ref OcclusionHits[j])).collider).transform.IsChildOf(portal);
				}
				if (!flag2)
				{
					return false;
				}
			}
			return true;
		}

		private static void DetachViews()
		{
			foreach (PortalView value in Views.Values)
			{
				value.Detach();
			}
			PortalVortex.RestoreAll();
		}

		private static bool StepImageCapture()
		{
			if (_captureSteps == null)
			{
				if (!PortalView.Reconstruct)
				{
					return false;
				}
				foreach (ViewCandidate item in Renderable)
				{
					if (item.Snapshot.ImageCapture == null && !item.Snapshot.IsDisposed)
					{
						_capturing = item.Snapshot;
						break;
					}
				}
				if (_capturing == null)
				{
					foreach (ViewCandidate item2 in Renderable)
					{
						PortalCapture imageCapture = item2.Snapshot.ImageCapture;
						if (imageCapture != null && imageCapture.LightingVersion != item2.Snapshot.LightingVersion && !item2.Snapshot.IsDisposed)
						{
							_capturing = item2.Snapshot;
							break;
						}
					}
				}
				if (_capturing == null)
				{
					return false;
				}
				_captureSteps = PortalCapture.BuildIncremental(_capturing, 1024);
			}
			if (_capturing.IsDisposed)
			{
				StopImageCapture();
				return false;
			}
			if (!_captureSteps.MoveNext())
			{
				StopImageCapture();
				return true;
			}
			if (_captureSteps.Current == null)
			{
				return true;
			}
			if (_captureSteps.Current.LightingVersion != _capturing.LightingVersion)
			{
				_captureSteps.Current.Dispose();
				StopImageCapture();
				return true;
			}
			_capturing.ImageCapture?.Dispose();
			_capturing.ImageCapture = _captureSteps.Current;
			_capturing.LastViewedAt = Time.realtimeSinceStartup;
			StopImageCapture();
			return true;
		}

		private static void StopImageCapture()
		{
			_captureSteps?.Dispose();
			_captureSteps = null;
			_capturing = null;
		}

		private static void ReleaseIdleCaptures()
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			foreach (Endpoint value in Endpoints.Values)
			{
				PortalSnapshot snapshot = value.Snapshot;
				if (snapshot?.ImageCapture != null && !(realtimeSinceStartup - snapshot.LastViewedAt < CaptureIdleSeconds))
				{
					snapshot.ImageCapture.Dispose();
					snapshot.ImageCapture = null;
				}
			}
		}

		private static void HealLostGeometry()
		{
			//IL_006a: 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)
			foreach (Endpoint value in Endpoints.Values)
			{
				if (value.Snapshot != null && value.Snapshot.LostGeometry && value != _loading)
				{
					bool flag = File.Exists(PortalSnapshot.ArchivePath(ArchiveDirectory, ((object)Unsafe.As<ZDOID, ZDOID>(ref value.Id)/*cast due to .constrained prefix*/).ToString()));
					PortalPreviewPlugin.Diag($"[portal preview] Scene of {value.Id} lost meshes the game unloaded; " + (flag ? "reloading it from disk" : "capturing it again"));
					StallProfile.Note($"{value.Id} lost geometry, " + (flag ? "reloading" : "recapturing"));
					Invalidate(value);
					value.SkipArchiveLoad = false;
					if (!flag)
					{
						Defer(value);
						value.StableAt = Time.realtimeSinceStartup + 1f;
					}
				}
			}
		}

		private static void FollowTimeOfDay(Endpoint endpoint, bool fresh = false)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			PortalSnapshot snapshot = endpoint.Snapshot;
			if (snapshot == null || snapshot.IsDisposed)
			{
				return;
			}
			if (!PortalPreviewPlugin.Cfg.FollowTimeOfDay.Value)
			{
				if (snapshot.AppliedLighting.HasValue)
				{
					snapshot.RestoreCapturedLighting();
				}
			}
			else
			{
				if (_lightingFailed)
				{
					return;
				}
				Lighting lighting;
				try
				{
					if (!DestinationLighting.TryCompute(snapshot.Position, out lighting))
					{
						return;
					}
				}
				catch (Exception ex)
				{
					_lightingFailed = true;
					PortalPreviewPlugin.Log.LogError((object)("[portal preview] The lighting at portal destinations could not be worked out; views keep the lighting they were captured with this session. " + ex));
					return;
				}
				if (fresh)
				{
					snapshot.TakeLightingAsCurrent(in lighting);
					return;
				}
				Lighting? appliedLighting = snapshot.AppliedLighting;
				if (!appliedLighting.HasValue || (lighting.Differs(appliedLighting.GetValueOrDefault()) && !(Time.realtimeSinceStartup - snapshot.RelitAt < 10f)))
				{
					snapshot.Relight(in lighting);
					StallProfile.Note($"{endpoint.Id} relit");
					PortalPreviewPlugin.Diag($"[portal preview] Scene of {endpoint.Id} relit: {lighting.Environment}, " + $"day fraction {EnvMan.instance.GetDayFraction():F3}, sun {lighting.SunIntensity:F2}");
				}
			}
		}

		private static void Maintain()
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			HealLostGeometry();
			StallProfile.Mark("heal lost geometry");
			ReleaseIdleCaptures();
			StallProfile.Mark("release idle captures");
			foreach (Endpoint value in Endpoints.Values)
			{
				FollowTimeOfDay(value);
			}
			StallProfile.Mark("time of day");
			Stale.Clear();
			foreach (Endpoint value2 in Endpoints.Values)
			{
				ZDOMan instance = ZDOMan.instance;
				ZDO val = ((instance != null) ? instance.GetZDO(value2.Id) : null);
				if (val != null && val.GetConnectionZDOID((ConnectionType)1) != value2.Target)
				{
					value2.Target = val.GetConnectionZDOID((ConnectionType)1);
					value2.Reported = false;
					if (value2.Target != ZDOID.None && (Object)(object)value2.Portal != (Object)null)
					{
						RequestFirstLinkCapture(value2, "connection changed in portal data", CaptureWhen.AtTeleport);
					}
				}
				else if (value2 != _loading && !VisibleTargets.Contains(value2.Id) && (Object)(object)value2.Portal == (Object)null && value2.Snapshot == null && !value2.Refresh && string.IsNullOrEmpty(value2.LastError) && value2.Id != _arrival)
				{
					Stale.Add(value2.Id);
				}
			}
			foreach (ZDOID item in Stale)
			{
				Endpoints.Remove(item);
			}
			StallProfile.Mark("links");
		}

		private static void CaptureOne()
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			List<Endpoint> list = new List<Endpoint>();
			foreach (Endpoint value in Endpoints.Values)
			{
				if (!((Object)(object)value.Portal == (Object)null) && value.Refresh)
				{
					Vector3 val = ((Component)Player.m_localPlayer).transform.position - ((Component)value.Portal).transform.position;
					if (((Vector3)(ref val)).sqrMagnitude <= 576f || (value.FirstLinkCapture && (Object)(object)ZNetScene.instance != (Object)null && ZNetScene.instance.IsAreaReady(((Component)value.Portal).transform.position)))
					{
						list.Add(value);
					}
				}
			}
			list.Sort(delegate(Endpoint a, Endpoint b)
			{
				//IL_000a: Unknown result type (might be due to invalid IL or missing references)
				//IL_001a: Unknown result type (might be due to invalid IL or missing references)
				//IL_001f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				//IL_0039: Unknown result type (might be due to invalid IL or missing references)
				//IL_0049: Unknown result type (might be due to invalid IL or missing references)
				//IL_004e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0053: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val2 = ((Component)Player.m_localPlayer).transform.position - ((Component)a.Portal).transform.position;
				float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
				val2 = ((Component)Player.m_localPlayer).transform.position - ((Component)b.Portal).transform.position;
				return sqrMagnit