Decompiled source of ShorterLoading v1.0.0

ShorterLoading.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using SoftReferenceableAssets;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("ShorterLoading")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+20e02409ee2dc88966c1aebdc478bcdbec01577e")]
[assembly: AssemblyProduct("ShorterLoading")]
[assembly: AssemblyTitle("ShorterLoading")]
[assembly: AssemblyVersion("1.0.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 ShorterLoading
{
	[HarmonyPatch(typeof(AltBiomeWorldData), "GenerateBiomePoints")]
	internal static class Patch_AltBiomeWorldData_GenerateBiomePoints
	{
		private const int Size = 2048;

		private static readonly FieldRef<AltBiomeWorldData, World> _world = Reflect.Try(() => AccessTools.FieldRefAccess<AltBiomeWorldData, World>("m_world"), "AltBiomeWorldData.m_world", "the biome map is computed the game's way");

		internal static bool Active
		{
			get
			{
				Settings cfg = ShorterLoadingPlugin.Cfg;
				if (cfg.Enabled.Value && cfg.FastBiomeMap.Value)
				{
					return !cfg.CheckBiomeMap.Value;
				}
				return false;
			}
		}

		private static bool Prepare()
		{
			return _world != null;
		}

		private static bool Prefix(World world)
		{
			EarlyBiomeMap earlyBiomeMap = EarlyBiomeMap.Take(world);
			if (!Active)
			{
				return true;
			}
			if (earlyBiomeMap != null && earlyBiomeMap.TryFinish(out var data))
			{
				Use(world, data);
				return false;
			}
			try
			{
				long timestamp = Stopwatch.GetTimestamp();
				Use(world, Build(WorldGenerator.instance));
				if (ShorterLoadingPlugin.Cfg.LogTiming.Value)
				{
					ShorterLoadingPlugin.Log.LogInfo((object)$"Biome map computed on {Environment.ProcessorCount} cores in {Clock.Seconds(Clock.Ms(timestamp) / 1000.0)}");
				}
				return false;
			}
			catch (Exception ex)
			{
				ShorterLoadingPlugin.Log.LogWarning((object)("Biome map on all cores failed, the game computes it: " + (ex.GetBaseException()?.Message ?? ex.Message)));
				return true;
			}
		}

		private static void Use(World world, AltBiomeWorldData data)
		{
			world.m_biomeData = data;
			_world.Invoke(data) = world;
		}

		internal static AltBiomeWorldData Build(WorldGenerator generator)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			AltBiomeWorldData val = new AltBiomeWorldData(2048);
			val.dt = DateTime.Now;
			BiomeIndex[,] biomes = val.PointBiomes;
			float[,] heights = val.PointHeights;
			Parallel.For(0, 2048, delegate(int i)
			{
				//IL_000e: 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_0031: Unknown result type (might be due to invalid IL or missing references)
				//IL_0071: Unknown result type (might be due to invalid IL or missing references)
				//IL_0077: Unknown result type (might be due to invalid IL or missing references)
				//IL_0083: Unknown result type (might be due to invalid IL or missing references)
				//IL_0088: 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_0092: Unknown result type (might be due to invalid IL or missing references)
				//IL_0097: Unknown result type (might be due to invalid IL or missing references)
				//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
				//IL_004d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0052: Unknown result type (might be due to invalid IL or missing references)
				RiverLookup.Enter();
				try
				{
					Color val3 = default(Color);
					for (int j = 0; j < 2048; j++)
					{
						Vector2 val2 = new Vector2
						{
							x = AltBiomeWorldData.MapSpaceToWorldSpace((float)j),
							y = AltBiomeWorldData.MapSpaceToWorldSpace((float)i)
						};
						if (((Vector2)(ref val2)).sqrMagnitude > 110250000f)
						{
							biomes[j, i] = BiomeHelpers.ToBiomeIndex((Biome)256);
							heights[j, i] = -1000f;
						}
						else
						{
							Biome biome = generator.GetBiome(val2.x, val2.y, 0.02f, false);
							biomes[j, i] = BiomeHelpers.ToBiomeIndex(biome);
							heights[j, i] = generator.GetBiomeHeight(biome, val2.x, val2.y, ref val3, false, true);
						}
					}
				}
				finally
				{
					RiverLookup.Exit();
				}
			});
			val.PointsGenerated = true;
			return val;
		}
	}
	internal sealed class EarlyBiomeMap
	{
		private static EarlyBiomeMap _pending;

		private readonly World _forWorld;

		private readonly WorldGenerator _generator;

		private readonly Task<AltBiomeWorldData> _task;

		private readonly long _start = Stopwatch.GetTimestamp();

		private double _computeMs = -1.0;

		private EarlyBiomeMap(World world, WorldGenerator generator)
		{
			EarlyBiomeMap earlyBiomeMap = this;
			_forWorld = world;
			_generator = generator;
			_task = Task.Run(delegate
			{
				long timestamp = Stopwatch.GetTimestamp();
				AltBiomeWorldData result = Patch_AltBiomeWorldData_GenerateBiomePoints.Build(generator);
				earlyBiomeMap._computeMs = Clock.Ms(timestamp);
				return result;
			});
		}

		internal static void Start(World world)
		{
			_pending = null;
			Settings cfg = ShorterLoadingPlugin.Cfg;
			WorldGenerator instance = WorldGenerator.instance;
			if (world != null && !world.m_menu && instance != null && Patch_AltBiomeWorldData_GenerateBiomePoints.Active && cfg.EarlyBiomeMap.Value)
			{
				_pending = new EarlyBiomeMap(world, instance);
			}
		}

		internal static EarlyBiomeMap Take(World world)
		{
			EarlyBiomeMap pending = _pending;
			_pending = null;
			if (pending == null || pending._forWorld != world || pending._generator != WorldGenerator.instance)
			{
				return null;
			}
			return pending;
		}

		internal bool TryFinish(out AltBiomeWorldData data)
		{
			long timestamp = Stopwatch.GetTimestamp();
			try
			{
				data = _task.Result;
			}
			catch (Exception ex)
			{
				data = null;
				ShorterLoadingPlugin.Log.LogWarning((object)("Early biome map failed, computing it now: " + (ex.GetBaseException()?.Message ?? ex.Message)));
				return false;
			}
			if (!ShorterLoadingPlugin.Cfg.LogTiming.Value)
			{
				return true;
			}
			double num = Clock.Ms(timestamp);
			double num2 = Clock.Ms(_start);
			ShorterLoadingPlugin.Log.LogInfo((object)($"Biome map computed early on {Environment.ProcessorCount} cores in {Clock.Seconds(_computeMs / 1000.0)}, started " + Clock.Seconds((num2 - num) / 1000.0) + " before the world load asked for it; the world load waited " + Clock.Seconds(num / 1000.0)));
			return true;
		}
	}
	[HarmonyPatch(typeof(WorldGenerator), "Initialize")]
	internal static class Patch_WorldGenerator_Initialize
	{
		private static void Postfix(World world)
		{
			try
			{
				EarlyBiomeMap.Start(world);
			}
			catch (Exception ex)
			{
				ShorterLoadingPlugin.Log.LogWarning((object)("Early biome map not started: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(AltBiomeWorldData), "GenerateBiomePoints")]
	internal static class Patch_AltBiomeWorldData_GenerateBiomePoints_Check
	{
		private const int Size = 2048;

		private static AltBiomeWorldData _ours;

		private static double _oursMs;

		private static long _gameStart;

		private static void Prefix()
		{
			_ours = null;
			Settings cfg = ShorterLoadingPlugin.Cfg;
			if (!cfg.CheckBiomeMap.Value)
			{
				return;
			}
			if (!cfg.Enabled.Value || !cfg.FastBiomeMap.Value)
			{
				ShorterLoadingPlugin.Log.LogWarning((object)"Biome map check NOT RUN: it needs ShorterLoading and 'Biome map on all cores' on");
				return;
			}
			try
			{
				long timestamp = Stopwatch.GetTimestamp();
				_ours = Patch_AltBiomeWorldData_GenerateBiomePoints.Build(WorldGenerator.instance);
				_oursMs = Clock.Ms(timestamp);
			}
			catch (Exception ex)
			{
				ShorterLoadingPlugin.Log.LogWarning((object)("Biome map check NOT FINISHED: computing the map on all cores threw: " + (ex.GetBaseException()?.Message ?? ex.Message) + " (the game's map is used)"));
			}
			_gameStart = Stopwatch.GetTimestamp();
		}

		private static void Finalizer(World world, Exception __exception)
		{
			AltBiomeWorldData ours = _ours;
			_ours = null;
			if (ours == null)
			{
				return;
			}
			if (__exception != null)
			{
				ShorterLoadingPlugin.Log.LogWarning((object)("Biome map check NOT FINISHED: the game's own computation threw: " + __exception.Message));
				return;
			}
			try
			{
				Compare(ours, world.m_biomeData, Clock.Ms(_gameStart));
			}
			catch (Exception ex)
			{
				ShorterLoadingPlugin.Log.LogWarning((object)("Biome map check NOT FINISHED: comparing the maps threw: " + ex.Message));
			}
		}

		private static void Compare(AltBiomeWorldData ours, AltBiomeWorldData game, double gameMs)
		{
			//IL_0016: 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)
			int num = 0;
			int num2 = 0;
			for (int i = 0; i < 2048; i++)
			{
				for (int j = 0; j < 2048; j++)
				{
					if (ours.PointBiomes[j, i] != game.PointBiomes[j, i])
					{
						num++;
					}
					if (!ours.PointHeights[j, i].Equals(game.PointHeights[j, i]))
					{
						num2++;
					}
				}
			}
			string text = "all cores " + Clock.Seconds(_oursMs / 1000.0) + ", the game's way " + Clock.Seconds(gameMs / 1000.0) + " (the game's map is used)";
			if (num == 0 && num2 == 0)
			{
				ShorterLoadingPlugin.Log.LogInfo((object)$"Biome map check PASSED: all {4194304} points match; {text}");
				return;
			}
			ShorterLoadingPlugin.Log.LogWarning((object)$"Biome map check FAILED: {num} biomes and {num2} heights differ of {4194304} points; {text}");
		}
	}
	internal sealed class ConfigurationManagerAttributes
	{
		public bool? ReadOnly;
	}
	[HarmonyPatch(typeof(DungeonGenerator), "OnRoomLoaded")]
	internal static class Patch_DungeonGenerator_OnRoomLoaded_Spread
	{
		private delegate Room PlaceRoomFn(DungeonGenerator generator, RoomData room, Vector3 pos, Quaternion rot, RoomConnection from, SpawnMode mode);

		private readonly struct Placement
		{
			internal readonly RoomData Room;

			internal readonly Vector3 Position;

			internal readonly Quaternion Rotation;

			internal Placement(RoomData room, Vector3 position, Quaternion rotation)
			{
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				//IL_000f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0010: Unknown result type (might be due to invalid IL or missing references)
				Room = room;
				Position = position;
				Rotation = rotation;
			}
		}

		private const string Missing = "dungeon rooms are placed in one frame";

		private const float BudgetMs = 4f;

		private static readonly FieldRef<DungeonGenerator, int> _roomsToLoad = Reflect.Try(() => AccessTools.FieldRefAccess<DungeonGenerator, int>("m_roomsToLoad"), "DungeonGenerator.m_roomsToLoad", "dungeon rooms are placed in one frame");

		private static readonly FieldInfo _loadedRooms = AccessTools.Field(typeof(DungeonGenerator), "m_loadedRooms");

		private static readonly PlaceRoomFn _placeRoom = Reflect.Try(() => AccessTools.MethodDelegate<PlaceRoomFn>(AccessTools.Method(typeof(DungeonGenerator), "PlaceRoom", new Type[5]
		{
			typeof(RoomData),
			typeof(Vector3),
			typeof(Quaternion),
			typeof(RoomConnection),
			typeof(SpawnMode)
		}, (Type[])null), (object)null, true), "DungeonGenerator.PlaceRoom", "dungeon rooms are placed in one frame");

		private static readonly Action<DungeonGenerator> _release = Reflect.Try(() => AccessTools.MethodDelegate<Action<DungeonGenerator>>(AccessTools.Method(typeof(DungeonGenerator), "ReleaseHeldReferences", (Type[])null, (Type[])null), (object)null, true), "DungeonGenerator.ReleaseHeldReferences", "dungeon rooms are placed in one frame");

		private static readonly Type _placement = AccessTools.Inner(typeof(DungeonGenerator), "RoomPlacementData");

		private static readonly FieldInfo _roomData = ((_placement == null) ? null : AccessTools.Field(_placement, "m_roomData"));

		private static readonly FieldInfo _position = ((_placement == null) ? null : AccessTools.Field(_placement, "m_position"));

		private static readonly FieldInfo _rotation = ((_placement == null) ? null : AccessTools.Field(_placement, "m_rotation"));

		private static bool Prepare()
		{
			if (_loadedRooms == null || _roomData == null || _position == null || _rotation == null)
			{
				ShorterLoadingPlugin.Log.LogError((object)"DungeonGenerator room data unavailable; dungeon rooms are placed in one frame");
				return false;
			}
			if (_roomsToLoad != null && _placeRoom != null)
			{
				return _release != null;
			}
			return false;
		}

		private static bool Prefix(DungeonGenerator __instance, LoadResult result)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			if (!ShorterLoadingPlugin.IsApplied(typeof(Patch_DungeonGenerator_OnRoomLoaded_Spread)))
			{
				return true;
			}
			try
			{
				if ((int)result != 0 || (Object)(object)__instance == (Object)null || _roomsToLoad.Invoke(__instance) != 1 || Budget() <= 0f)
				{
					return true;
				}
				List<Placement> rooms = Rooms(__instance);
				_roomsToLoad.Invoke(__instance) = 0;
				((MonoBehaviour)__instance).StartCoroutine(Place(__instance, rooms));
				return false;
			}
			catch (Exception ex)
			{
				ShorterLoadingPlugin.StandDown(typeof(Patch_DungeonGenerator_OnRoomLoaded_Spread), "Dungeon spread", ex);
				return true;
			}
		}

		private static float Budget()
		{
			Settings cfg = ShorterLoadingPlugin.Cfg;
			Player localPlayer = Player.m_localPlayer;
			if (!cfg.Enabled.Value || !cfg.DungeonSpread.Value || SteadyFrame.ObjectStreaming || !((Object)(object)localPlayer != (Object)null) || ((Character)localPlayer).IsTeleporting())
			{
				return 0f;
			}
			return 4f;
		}

		private static List<Placement> Rooms(DungeonGenerator generator)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Expected O, but got Unknown
			Array obj = (Array)_loadedRooms.GetValue(generator);
			List<Placement> list = new List<Placement>(obj.Length);
			foreach (object item in obj)
			{
				list.Add(new Placement((RoomData)_roomData.GetValue(item), (Vector3)_position.GetValue(item), (Quaternion)_rotation.GetValue(item)));
			}
			return list;
		}

		private static IEnumerator Place(DungeonGenerator generator, List<Placement> rooms)
		{
			long timestamp = Stopwatch.GetTimestamp();
			for (int i = 0; i < rooms.Count; i++)
			{
				float num = Budget();
				if (i > 0 && num > 0f && Clock.Ms(timestamp) >= (double)num)
				{
					yield return null;
					if ((Object)(object)generator == (Object)null)
					{
						yield break;
					}
					timestamp = Stopwatch.GetTimestamp();
				}
				if (!PlaceOne(generator, rooms[i]))
				{
					break;
				}
			}
			Finish(generator);
		}

		private static bool PlaceOne(DungeonGenerator generator, Placement room)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				_placeRoom(generator, room.Room, room.Position, room.Rotation, null, (SpawnMode)1);
				return true;
			}
			catch (Exception ex)
			{
				ShorterLoadingPlugin.Log.LogError((object)("Dungeon spread: placing a room of " + ((Object)generator).name + " failed: " + ex.Message));
				return false;
			}
		}

		private static void Finish(DungeonGenerator generator)
		{
			try
			{
				SnapToGround.SnappAll();
				_loadedRooms.SetValue(generator, null);
			}
			finally
			{
				_release(generator);
			}
		}
	}
	[HarmonyPatch(typeof(DungeonGenerator), "OnDestroy")]
	internal static class Patch_DungeonGenerator_OnDestroy_Guard
	{
		private static readonly FieldRef<DungeonGenerator, ZDO> _loadingMark = Reflect.Try(() => AccessTools.FieldRefAccess<DungeonGenerator, ZDO>("m_zdoSetToBeLoadingInZone"), "DungeonGenerator.m_zdoSetToBeLoadingInZone", "leaving the world while a dungeon is placed logs an error");

		private static bool Prepare()
		{
			return _loadingMark != null;
		}

		private static void Prefix(DungeonGenerator __instance)
		{
			if ((Object)(object)ZoneSystem.instance == (Object)null)
			{
				_loadingMark.Invoke(__instance) = null;
			}
		}
	}
	[HarmonyPatch]
	internal static class Patch_ClutterSystem_GrassBudget
	{
		private const string Missing = "grass is rebuilt in one frame";

		private const float BudgetMs = 3f;

		private static readonly FieldRef<ClutterSystem, bool> _forceRebuild = Reflect.Try(() => AccessTools.FieldRefAccess<ClutterSystem, bool>("m_forceRebuild"), "ClutterSystem.m_forceRebuild", "grass is rebuilt in one frame");

		private static readonly FieldRef<ClutterSystem, IDictionary> _patches = Reflect.Try(() => AccessTools.FieldRefAccess<ClutterSystem, IDictionary>("m_patches"), "ClutterSystem.m_patches", "grass is rebuilt in one frame");

		private static readonly Type _patchData = AccessTools.Inner(typeof(ClutterSystem), "PatchData");

		private static readonly FieldRef<object, bool> _reset = Reflect.Try(() => AccessTools.FieldRefAccess<bool>(_patchData, "m_reset"), "ClutterSystem.PatchData.m_reset", "grass is rebuilt in one frame");

		private static readonly FieldRef<object, float> _timer = Reflect.Try(() => AccessTools.FieldRefAccess<float>(_patchData, "m_timer"), "ClutterSystem.PatchData.m_timer", "grass is rebuilt in one frame");

		private static bool _budgeting;

		private static bool _unfinished;

		private static long _deadline;

		private static bool Prepare()
		{
			if (_forceRebuild != null && _patches != null && _reset != null)
			{
				return _timer != null;
			}
			return false;
		}

		[HarmonyPatch(typeof(ClutterSystem), "GeneratePatches")]
		[HarmonyPrefix]
		private static void GeneratePatchesPrefix(bool rebuildAll)
		{
			_budgeting = false;
			if (!rebuildAll || !ShorterLoadingPlugin.IsApplied(typeof(Patch_ClutterSystem_GrassBudget)))
			{
				return;
			}
			try
			{
				Settings cfg = ShorterLoadingPlugin.Cfg;
				Player localPlayer = Player.m_localPlayer;
				if (cfg.Enabled.Value && cfg.GrassBudget.Value && !SteadyFrame.Grass && (Object)(object)localPlayer != (Object)null && !((Character)localPlayer).IsTeleporting())
				{
					_budgeting = true;
					_unfinished = false;
					_deadline = Stopwatch.GetTimestamp() + Clock.Ticks(3.0);
				}
			}
			catch (Exception ex)
			{
				ShorterLoadingPlugin.StandDown(typeof(Patch_ClutterSystem_GrassBudget), "Grass rebuild budget", ex);
			}
		}

		[HarmonyPatch(typeof(ClutterSystem), "GeneratePatch")]
		[HarmonyPrefix]
		private static bool GeneratePatchPrefix(ClutterSystem __instance, Vector3 camPos, Vector2Int p)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: 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)
			if (!_budgeting || Stopwatch.GetTimestamp() < _deadline)
			{
				return true;
			}
			try
			{
				if (Utils.DistanceXZ(__instance.GetVegPatchCenter(p), camPos) > __instance.m_distance)
				{
					return true;
				}
				object obj = _patches.Invoke(__instance)[p];
				if (obj != null && !_reset.Invoke(obj))
				{
					return true;
				}
				if (obj != null)
				{
					_timer.Invoke(obj) = 0f;
				}
				_unfinished = true;
				return false;
			}
			catch (Exception ex)
			{
				_budgeting = false;
				ShorterLoadingPlugin.StandDown(typeof(Patch_ClutterSystem_GrassBudget), "Grass rebuild budget", ex);
				return true;
			}
		}

		[HarmonyPatch(typeof(ClutterSystem), "GeneratePatches")]
		[HarmonyPostfix]
		private static void GeneratePatchesPostfix(ClutterSystem __instance)
		{
			if (_budgeting && _unfinished)
			{
				_forceRebuild.Invoke(__instance) = true;
			}
			_budgeting = false;
		}
	}
	internal static class LakeMerge
	{
		internal static List<T> Merge<T>(IList<T> input, float range, Func<T, float> x, Func<T, float> y, Func<T, T, float> distance, Func<T, T, bool> same, Func<T, T, T> midpoint)
		{
			int count = input.Count;
			T[] array = new T[count];
			int[] array2 = new int[count];
			int[] array3 = new int[count];
			for (int i = 0; i < count; i++)
			{
				array[i] = input[i];
				array2[i] = i;
				array3[i] = i;
			}
			double size = (double)range * 1.001 + 1.0;
			Dictionary<long, List<int>> squares = new Dictionary<long, List<int>>();
			long[] squareOf = new long[count];
			int[] inSquare = new int[count];
			for (int j = 0; j < count; j++)
			{
				long num = Key(Square(x(array[j]), size), Square(y(array[j]), size));
				if (!squares.TryGetValue(num, out var value))
				{
					value = new List<int>();
					squares.Add(num, value);
				}
				squareOf[j] = num;
				inSquare[j] = value.Count;
				value.Add(j);
			}
			int num2 = 0;
			int num3 = count;
			List<T> list = new List<T>();
			while (num3 > 0)
			{
				int num4 = array2[num2];
				T val = array[num4];
				Unfile(num4);
				num2++;
				for (num3--; num3 > 0; num3--)
				{
					int num5 = -1;
					float num6 = 99999f;
					int num7 = Square(x(val), size);
					int num8 = Square(y(val), size);
					for (int k = -1; k <= 1; k++)
					{
						for (int l = -1; l <= 1; l++)
						{
							if (!squares.TryGetValue(Key(num7 + l, num8 + k), out var value2))
							{
								continue;
							}
							foreach (int item in value2)
							{
								T val2 = array[item];
								if (!same(val2, val))
								{
									float num9 = distance(val, val2);
									if (num9 < range && (num9 < num6 || (num9 == num6 && num5 >= 0 && array3[item] < array3[num5])))
									{
										num5 = item;
										num6 = num9;
									}
								}
							}
						}
					}
					if (num5 < 0)
					{
						break;
					}
					val = midpoint(val, array[num5]);
					int num10 = array3[num5];
					array3[array2[num10] = array2[num2 + num3 - 1]] = num10;
					Unfile(num5);
				}
				list.Add(val);
			}
			return list;
			void Unfile(int id)
			{
				List<int> list2 = squares[squareOf[id]];
				int num11 = inSquare[id];
				int num12 = (list2[num11] = list2[list2.Count - 1]);
				inSquare[num12] = num11;
				list2.RemoveAt(list2.Count - 1);
			}
		}

		private static int Square(float value, double size)
		{
			return (int)Math.Floor((double)value / size);
		}

		private static long Key(int sx, int sy)
		{
			return ((long)sx << 32) ^ (uint)sy;
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "IsAreaReady")]
	internal static class Patch_ZNetScene_IsAreaReady_Hang
	{
		private const double WarnEveryMs = 10000.0;

		private static readonly List<ZDO> _objects = new List<ZDO>();

		private static long _lastWarning;

		private static void Postfix(ZNetScene __instance, Vector3 point, ref bool __result)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			if (__result || !ShorterLoadingPlugin.IsApplied(typeof(Patch_ZNetScene_IsAreaReady_Hang)))
			{
				return;
			}
			try
			{
				Settings cfg = ShorterLoadingPlugin.Cfg;
				if (cfg.Enabled.Value && cfg.LoadingHangFix.Value && !SteadyFrame.Housekeeping && OnlyStale(__instance, point, out var stale))
				{
					__result = true;
					Warn(stale);
				}
			}
			catch (Exception ex)
			{
				ShorterLoadingPlugin.StandDown(typeof(Patch_ZNetScene_IsAreaReady_Hang), "Loading hang fix", ex);
			}
		}

		private static bool OnlyStale(ZNetScene scene, Vector3 point, out int stale)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			stale = 0;
			Vector2s zone = ZoneSystem.GetZone(point);
			if (!ZoneSystem.instance.IsZoneLoaded(zone))
			{
				return false;
			}
			_objects.Clear();
			ZDOMan.instance.FindSectorObjects(zone, new SimulationDistance(1, 0, false), _objects, (List<ZDO>)null);
			try
			{
				foreach (ZDO @object in _objects)
				{
					int prefab = @object.GetPrefab();
					if (prefab != 0 && !((Object)(object)scene.GetPrefab(prefab) == (Object)null) && !Object.op_Implicit((Object)(object)scene.FindInstance(@object)))
					{
						if (!scene.HaveInstance(@object))
						{
							return false;
						}
						stale++;
					}
				}
				return stale > 0;
			}
			finally
			{
				_objects.Clear();
			}
		}

		private static void Warn(int stale)
		{
			long timestamp = Stopwatch.GetTimestamp();
			if (_lastWarning == 0L || timestamp - _lastWarning >= Clock.Ticks(10000.0))
			{
				_lastWarning = timestamp;
				ShorterLoadingPlugin.Log.LogWarning((object)($"{stale} object(s) near the spawn point were destroyed without the game being told; counting the area as ready " + "so the loading screen can lift"));
			}
		}
	}
	internal static class LoadingScreen
	{
		internal static bool RespawnPending()
		{
			if ((Object)(object)Player.m_localPlayer == (Object)null && (Object)(object)Game.instance != (Object)null)
			{
				return Game.instance.WaitingForRespawn();
			}
			return false;
		}

		internal static bool Loading()
		{
			if (!RespawnPending())
			{
				return PortalArriving();
			}
			return true;
		}

		internal static bool PortalArriving()
		{
			Settings cfg = ShorterLoadingPlugin.Cfg;
			if (!SteadyFrame.PortalLoading && cfg.PortalRush.Value)
			{
				return Portal.Arriving();
			}
			return false;
		}

		internal static bool CanRush()
		{
			if (ShorterLoadingPlugin.Cfg.Enabled.Value && Loading())
			{
				return Black();
			}
			return false;
		}

		internal static bool Black()
		{
			if (((Object)(object)CinematicsManager.s_instance != (Object)null && CinematicsManager.IsPlaying()) || TextViewer.IsShowingIntro())
			{
				return false;
			}
			if (!((Object)(object)Hud.instance == (Object)null))
			{
				return Hud.instance.m_loadingScreen.alpha >= 0.99f;
			}
			return true;
		}
	}
	internal static class LoadTimeline
	{
		internal sealed class Step
		{
			private float _begin = -1f;

			private int _depth;

			internal float Seconds { get; private set; } = -1f;

			internal void Begin()
			{
				if (_depth++ == 0)
				{
					_begin = Now;
				}
			}

			internal void End()
			{
				if (_depth > 0 && --_depth == 0 && _begin >= 0f)
				{
					Seconds = Now - _begin;
					_begin = -1f;
				}
			}

			internal void Reset()
			{
				_begin = -1f;
				_depth = 0;
				Seconds = -1f;
			}
		}

		private static float _start = -1f;

		private static float _loadScene = -1f;

		private static float _awake = -1f;

		private static float _worldStart = -1f;

		private static float _worldEnd = -1f;

		private static float _collectStart = -1f;

		private static float _collectEnd = -1f;

		private static float _firstFrame = -1f;

		private static float _respawn = -1f;

		internal static readonly Step Rivers = new Step();

		internal static readonly Step Lakes = new Step();

		internal static readonly Step RiverPlan = new Step();

		internal static readonly Step Streams = new Step();

		internal static readonly Step NorthStreams = new Step();

		internal static readonly Step BiomeMap = new Step();

		internal static readonly Step BiomeSectors = new Step();

		internal static readonly Step Locations = new Step();

		private static readonly Step[] Steps = new Step[8] { Rivers, Lakes, RiverPlan, Streams, NorthStreams, BiomeMap, BiomeSectors, Locations };

		private static float Now => Time.realtimeSinceStartup;

		internal static void Started()
		{
			Clear();
			_start = Now;
		}

		internal static void Clear()
		{
			_start = -1f;
			_loadScene = (_awake = (_worldStart = (_worldEnd = (_collectStart = (_collectEnd = (_firstFrame = (_respawn = -1f)))))));
			Step[] steps = Steps;
			for (int i = 0; i < steps.Length; i++)
			{
				steps[i].Reset();
			}
		}

		internal static void LoadScene()
		{
			_loadScene = Now;
		}

		internal static void Awake()
		{
			_awake = Now;
		}

		internal static void WorldStart()
		{
			_worldStart = Now;
		}

		internal static void WorldEnd()
		{
			_worldEnd = Now;
		}

		internal static void CollectStart()
		{
			if (_awake >= 0f && _firstFrame < 0f)
			{
				_collectStart = Now;
			}
		}

		internal static void CollectEnd()
		{
			if (_collectStart >= 0f && _collectEnd < 0f && _firstFrame < 0f)
			{
				_collectEnd = Now;
			}
		}

		internal static void Frame()
		{
			if (_awake >= 0f && _firstFrame < 0f)
			{
				_firstFrame = Now;
			}
		}

		internal static void Respawn()
		{
			if (_awake >= 0f && _respawn < 0f)
			{
				_respawn = Now;
			}
		}

		internal static void Log()
		{
			if (!(_awake < 0f) && !(_respawn < 0f))
			{
				float num = Span(_collectStart, _collectEnd);
				float seconds = BiomeMap.Seconds;
				float seconds2 = BiomeSectors.Seconds;
				float seconds3 = Locations.Seconds;
				float num2 = Span(_worldStart, _worldEnd);
				if (num2 >= 0f)
				{
					num2 -= Math.Max(num, 0f) + Math.Max(seconds, 0f) + Math.Max(seconds3, 0f);
				}
				float num3 = Span(_awake, _worldStart);
				Settings cfg = ShorterLoadingPlugin.Cfg;
				string text = (cfg.CheckRivers.Value ? " with the river check, parts the game's way" : "");
				string text2 = (cfg.CheckBiomeMap.Value ? " with the biome map check" : "");
				string text3 = ((seconds >= 0f && seconds2 >= 0f) ? (" (points " + Clock.Seconds(seconds - seconds2) + ", sectors " + Clock.Seconds(seconds2) + ")") : "");
				ShorterLoadingPlugin.Log.LogInfo((object)("  Before the loading screen: menu fade " + Clock.Seconds(Span(_start, _loadScene)) + ", scene load " + Clock.Seconds(Span(_loadScene, _awake)) + ", scene setup " + Clock.Seconds(num3) + " (rivers " + Clock.Seconds(Rivers.Seconds) + text + ": lakes " + Clock.Seconds(Lakes.Seconds) + ", rivers between lakes " + Clock.Seconds(RiverPlan.Seconds) + ", streams " + Clock.Seconds(Streams.Seconds) + ", Deep North streams " + Clock.Seconds(NorthStreams.Seconds) + "), world file " + Clock.Seconds(num2) + ", unload unused assets " + Clock.Seconds(num) + ", biome map " + Clock.Seconds(seconds) + text2 + text3 + ", locations " + Clock.Seconds(seconds3) + ", then until the first frame " + Clock.Seconds(Span((_worldEnd >= 0f) ? _worldEnd : _awake, _firstFrame)) + "; first frame to respawn " + Clock.Seconds(Gap(_firstFrame, _respawn)) + "; " + Clock.Seconds(Span((_start >= 0f) ? _start : _awake, _respawn)) + " in all since " + ((_start >= 0f) ? "pressing Start" : "the game scene loaded")));
				_awake = -1f;
				_start = -1f;
			}
		}

		private static float Span(float from, float to)
		{
			if (!(from >= 0f) || !(to >= from))
			{
				return -1f;
			}
			return to - from;
		}

		private static float Gap(float from, float to)
		{
			if (!(from >= 0f) || !(to >= 0f))
			{
				return -1f;
			}
			return Math.Max(0f, to - from);
		}
	}
	[TimingPatch]
	[HarmonyPatch(typeof(FejdStartup), "TransitionToMainScene")]
	internal static class Patch_FejdStartup_TransitionToMainScene
	{
		private static void Prefix()
		{
			LoadTimeline.Started();
		}
	}
	[TimingPatch]
	[HarmonyPatch(typeof(FejdStartup), "LoadMainScene")]
	internal static class Patch_FejdStartup_LoadMainScene
	{
		private static void Prefix()
		{
			LoadTimeline.LoadScene();
		}
	}
	[TimingPatch]
	[HarmonyPatch(typeof(ZNet), "ServerLoadWorld")]
	internal static class Patch_ZNet_ServerLoadWorld
	{
		private static void Prefix()
		{
			LoadTimeline.WorldStart();
		}

		private static void Postfix()
		{
			LoadTimeline.WorldEnd();
		}
	}
	[TimingPatch]
	[HarmonyPatch(typeof(Game), "CollectResources")]
	internal static class Patch_Game_CollectResources
	{
		private static void Prefix()
		{
			LoadTimeline.CollectStart();
		}

		private static void Postfix()
		{
			LoadTimeline.CollectEnd();
		}
	}
	[TimingPatch]
	[HarmonyPatch(typeof(Game), "Update")]
	internal static class Patch_Game_Update
	{
		private static void Prefix()
		{
			LoadTimeline.Frame();
		}
	}
	[TimingPatch]
	[HarmonyPatch(typeof(WorldGenerator), "Pregenerate")]
	internal static class Patch_WorldGenerator_Pregenerate
	{
		[HarmonyPriority(800)]
		private static void Prefix()
		{
			LoadTimeline.Rivers.Begin();
		}

		private static void Postfix()
		{
			LoadTimeline.Rivers.End();
		}
	}
	[TimingPatch]
	[HarmonyPatch(typeof(WorldGenerator), "FindLakes")]
	internal static class Patch_WorldGenerator_FindLakes
	{
		private static void Prefix()
		{
			LoadTimeline.Lakes.Begin();
		}

		private static void Postfix()
		{
			LoadTimeline.Lakes.End();
		}
	}
	[TimingPatch]
	[HarmonyPatch(typeof(WorldGenerator), "PlaceRivers")]
	internal static class Patch_WorldGenerator_PlaceRivers
	{
		private static void Prefix()
		{
			LoadTimeline.RiverPlan.Begin();
		}

		private static void Postfix()
		{
			LoadTimeline.RiverPlan.End();
		}
	}
	[TimingPatch]
	[HarmonyPatch(typeof(WorldGenerator), "PlaceStreams")]
	internal static class Patch_WorldGenerator_PlaceStreams
	{
		private static void Prefix(bool isDN)
		{
			(isDN ? LoadTimeline.NorthStreams : LoadTimeline.Streams).Begin();
		}

		private static void Postfix(bool isDN)
		{
			(isDN ? LoadTimeline.NorthStreams : LoadTimeline.Streams).End();
		}
	}
	[TimingPatch]
	[HarmonyPatch(typeof(AltBiomeWorldData), "VerifyBiomeData")]
	internal static class Patch_AltBiomeWorldData_VerifyBiomeData
	{
		private static void Prefix()
		{
			LoadTimeline.BiomeMap.Begin();
		}

		private static void Postfix()
		{
			LoadTimeline.BiomeMap.End();
		}
	}
	[TimingPatch]
	[HarmonyPatch(typeof(AltBiomeWorldData), "GenerateSectors")]
	internal static class Patch_AltBiomeWorldData_GenerateSectors
	{
		private static void Prefix()
		{
			LoadTimeline.BiomeSectors.Begin();
		}

		private static void Postfix()
		{
			LoadTimeline.BiomeSectors.End();
		}
	}
	[TimingPatch]
	[HarmonyPatch(typeof(ZoneSystem), "GenerateLocationsIfNeeded")]
	internal static class Patch_ZoneSystem_GenerateLocationsIfNeeded
	{
		private static void Prefix()
		{
			LoadTimeline.Locations.Begin();
		}

		private static void Postfix()
		{
			LoadTimeline.Locations.End();
		}
	}
	[HarmonyPatch]
	internal static class Patch_MapTeleport
	{
		private static Player _player;

		private static bool _aimed;

		private static bool Active
		{
			get
			{
				Settings cfg = ShorterLoadingPlugin.Cfg;
				if (cfg.Enabled.Value)
				{
					return cfg.MapTeleport.Value;
				}
				return false;
			}
		}

		private static bool Prepare()
		{
			return Portal.Target != null;
		}

		internal static bool Aimed(Player player)
		{
			if ((Object)(object)_player != (Object)null && (Object)(object)player == (Object)(object)_player)
			{
				return _aimed;
			}
			return false;
		}

		[HarmonyPatch(typeof(Minimap), "DebugTeleport")]
		[HarmonyPrefix]
		private static void DebugTeleportPrefix(out bool __state)
		{
			Player localPlayer = Player.m_localPlayer;
			__state = (Object)(object)localPlayer != (Object)null && ((Character)localPlayer).IsTeleporting();
		}

		[HarmonyPatch(typeof(Minimap), "DebugTeleport")]
		[HarmonyPostfix]
		private static void DebugTeleportPostfix(bool __state)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			if (__state || !ShorterLoadingPlugin.IsApplied(typeof(Patch_MapTeleport)))
			{
				return;
			}
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if (Active && (Object)(object)localPlayer != (Object)null && ((Character)localPlayer).IsTeleporting())
				{
					_player = localPlayer;
					float num = default(float);
					_aimed = Heightmap.GetHeight(Portal.Target.Invoke(localPlayer), ref num);
				}
			}
			catch (Exception ex)
			{
				_player = null;
				ShorterLoadingPlugin.StandDown(typeof(Patch_MapTeleport), "Map teleport", ex);
			}
		}

		[HarmonyPatch(typeof(Player), "UpdateTeleport")]
		[HarmonyPrefix]
		[HarmonyPriority(600)]
		private static void UpdateTeleportPrefix(Player __instance)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_player == (Object)null || (Object)(object)__instance != (Object)(object)_player)
			{
				return;
			}
			try
			{
				if (!((Character)__instance).IsTeleporting() || !Active || !ShorterLoadingPlugin.IsApplied(typeof(Patch_MapTeleport)))
				{
					_player = null;
					return;
				}
				ref Vector3 reference = ref Portal.Target.Invoke(__instance);
				float num = default(float);
				if (!_aimed && Heightmap.GetHeight(reference, ref num))
				{
					reference.y = Mathf.Max(0f, num);
					_aimed = true;
				}
			}
			catch (Exception ex)
			{
				_player = null;
				ShorterLoadingPlugin.StandDown(typeof(Patch_MapTeleport), "Map teleport", ex);
			}
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "CreateObjectsSorted")]
	internal static class Patch_ZNetScene_CreateObjectsSorted
	{
		private static readonly List<ZDO> _near = new List<ZDO>();

		private static double _msPerObject = -1.0;

		private static bool _loading;

		internal static bool Backlog { get; private set; }

		private static bool Active => ShorterLoadingPlugin.IsApplied(typeof(Patch_ZNetScene_CreateObjectsSorted));

		private static void Prefix(ref List<ZDO> currentNearObjects, ref int maxCreatedPerFrame, out long __state)
		{
			__state = 0L;
			if (!Active)
			{
				return;
			}
			try
			{
				if (Before(ref currentNearObjects, ref maxCreatedPerFrame))
				{
					__state = Stopwatch.GetTimestamp();
				}
			}
			catch (Exception ex)
			{
				StandDown(ex);
			}
		}

		private static void Postfix(List<ZDO> currentNearObjects, int maxCreatedPerFrame, ref int created, long __state)
		{
			if (__state == 0L || !Active)
			{
				return;
			}
			try
			{
				After(currentNearObjects, maxCreatedPerFrame, created, Clock.Ms(__state));
			}
			catch (Exception ex)
			{
				StandDown(ex);
			}
		}

		private static void StandDown(Exception ex)
		{
			_loading = false;
			Backlog = false;
			ShorterLoadingPlugin.StandDown(typeof(Patch_ZNetScene_CreateObjectsSorted), "Object rush and load less first", ex);
		}

		internal static bool Rushing()
		{
			if (ShorterLoadingPlugin.Cfg.ObjectRush.Value)
			{
				return LoadingScreen.CanRush();
			}
			return false;
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static bool Before(ref List<ZDO> near, ref int limit)
		{
			Settings cfg = ShorterLoadingPlugin.Cfg;
			_loading = cfg.Enabled.Value && LoadingScreen.Loading();
			if (!_loading)
			{
				Backlog = false;
				return SpawnTiming.Measuring;
			}
			if (cfg.LoadLessFirst.Value && ZoneSystem.instance.IsActiveAreaLoaded())
			{
				near = Narrow(near, cfg.LoadLessRadius.Value);
			}
			if (cfg.ObjectRush.Value && LoadingScreen.Black())
			{
				limit = SpawnRules.ObjectRushCount(cfg.ObjectRushBudget.Value, _msPerObject, limit, cfg.ObjectRushMax.Value);
			}
			return true;
		}

		private static List<ZDO> Narrow(List<ZDO> near, int radius)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: 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_0052: Unknown result type (might be due to invalid IL or missing references)
			Vector2s zone = ZoneSystem.GetZone(ZNet.instance.GetReferencePosition());
			_near.Clear();
			int num = 0;
			foreach (ZDO item in near)
			{
				if (!item.Created)
				{
					Vector2s sector = item.GetSector();
					if (SpawnRules.WithinZones(sector.x - zone.x, sector.y - zone.y, radius))
					{
						_near.Add(item);
					}
					else
					{
						num++;
					}
				}
			}
			SpawnTiming.Narrowed(num, radius);
			return _near;
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void After(List<ZDO> near, int limit, int created, double ms)
		{
			if (_loading)
			{
				_msPerObject = SpawnRules.UpdateMsPerObject(_msPerObject, created, ms);
				Backlog = created >= limit;
			}
			SpawnTiming.ObjectTick(created, limit, ms);
			Patch_Teleport_Timing.ObjectTick(created, limit, ms);
			SpawnTiming.AfterSpawnTick(near, created);
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "Update")]
	internal static class Patch_ZNetScene_Update_EveryFrame
	{
		private static readonly FieldRef<ZNetScene, float> _timer = Reflect.Try(() => AccessTools.FieldRefAccess<ZNetScene, float>("m_createDestroyTimer"), "ZNetScene.m_createDestroyTimer", "the object rush runs at the game's pass rate");

		private static bool Prepare()
		{
			return _timer != null;
		}

		private static void Prefix(ZNetScene __instance)
		{
			if (!Patch_ZNetScene_CreateObjectsSorted.Backlog || !ShorterLoadingPlugin.IsApplied(typeof(Patch_ZNetScene_Update_EveryFrame)))
			{
				return;
			}
			try
			{
				Apply(__instance);
			}
			catch (Exception ex)
			{
				ShorterLoadingPlugin.StandDown(typeof(Patch_ZNetScene_Update_EveryFrame), "Object rush every-frame passes", ex);
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void Apply(ZNetScene scene)
		{
			if (Patch_ZNetScene_CreateObjectsSorted.Rushing())
			{
				_timer.Invoke(scene) = 1f;
			}
		}
	}
	[TimingPatch]
	[HarmonyPatch(typeof(ZNetScene), "OnDestroy")]
	internal static class Patch_ZNetScene_OnDestroy
	{
		private static void Postfix()
		{
			SpawnTiming.StopRest("you logged out");
		}
	}
	[HarmonyPatch(typeof(Game), "_RequestRespawn")]
	internal static class Patch_Game_RequestRespawnStart
	{
		private static void Postfix(Game __instance)
		{
			SpawnGate.Reset();
			LoadTimeline.Respawn();
			SpawnTiming.Begin(SpawnGate.AfterDeath(__instance));
		}
	}
	[HarmonyPatch(typeof(Game), "FindSpawnPoint")]
	internal static class Patch_Game_FindSpawnPoint
	{
		private static readonly FieldRef<Game, float> _loadDuration = Reflect.Try(() => AccessTools.FieldRefAccess<Game, float>("m_respawnLoadDuration"), "Game.m_respawnLoadDuration", "the fixed wait is kept");

		private static bool Prepare()
		{
			if (_loadDuration != null)
			{
				return SpawnGate.Ready;
			}
			return false;
		}

		private static void Prefix(Game __instance, out float? __state)
		{
			float num = _loadDuration.Invoke(__instance);
			__state = num;
			_loadDuration.Invoke(__instance) = SpawnGate.WaitFor(__instance, num);
		}

		private static void Finalizer(Game __instance, float? __state, bool __result)
		{
			if (__state.HasValue)
			{
				_loadDuration.Invoke(__instance) = __state.Value;
				SpawnGate.Finish(__instance, __result, __state.Value);
			}
		}
	}
	[HarmonyPatch(typeof(Game), "RequestRespawn")]
	internal static class Patch_Game_RequestRespawn
	{
		private static void Prefix(ref float delay, bool afterDeath)
		{
			if (afterDeath)
			{
				Settings cfg = ShorterLoadingPlugin.Cfg;
				if (cfg.Enabled.Value)
				{
					delay = Mathf.Min(delay, cfg.DeathDelay.Value);
				}
				SpawnTiming.DeathDelay(delay);
			}
		}
	}
	[HarmonyPatch(typeof(Hud), "GetFadeDuration")]
	internal static class Patch_Hud_GetFadeDuration
	{
		private const float ArrivalWindow = 2f;

		private static Player _lastPlayer;

		private static bool _wasTeleporting;

		private static float _arrivedAt = -100f;

		private static void Postfix(Player player, ref float __result)
		{
			if (!ShorterLoadingPlugin.IsApplied(typeof(Patch_Hud_GetFadeDuration)))
			{
				return;
			}
			try
			{
				__result = Apply(player, __result);
			}
			catch (Exception ex)
			{
				ShorterLoadingPlugin.StandDown(typeof(Patch_Hud_GetFadeDuration), "Death, sleep and portal fade", ex);
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static float Apply(Player player, float fade)
		{
			bool flag = (Object)(object)player != (Object)null && ((Character)player).IsTeleporting();
			if ((Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)_lastPlayer && _wasTeleporting && !flag)
			{
				_arrivedAt = Time.time;
			}
			_lastPlayer = player;
			_wasTeleporting = flag;
			Settings cfg = ShorterLoadingPlugin.Cfg;
			if (!cfg.Enabled.Value || (Object)(object)player == (Object)null)
			{
				return fade;
			}
			if (((Character)player).IsDead())
			{
				if (!ShorterLoadingPlugin.IsApplied(typeof(Patch_Game_RequestRespawn)))
				{
					return fade;
				}
				return SpawnRules.DeathFade(fade, cfg.DeathDelay.Value);
			}
			if (cfg.FasterSleep.Value && (player.IsSleeping() || Patch_Player_SetSleeping.JustWoke(player)))
			{
				return Mathf.Min(fade, cfg.SleepFadeTime.Value);
			}
			if (!SteadyFrame.Transitions && (flag || Time.time - _arrivedAt < 2f))
			{
				return Mathf.Min(fade, cfg.PortalFadeTime.Value);
			}
			return fade;
		}
	}
	[TimingPatch]
	[HarmonyPatch(typeof(Game), "SpawnPlayer")]
	internal static class Patch_Game_SpawnPlayer
	{
		private static void Postfix()
		{
			SpawnTiming.End();
		}
	}
	[TimingPatch]
	[HarmonyPatch(typeof(Game), "Awake")]
	internal static class Patch_Game_Awake
	{
		private static void Postfix()
		{
			LoadTimeline.Awake();
		}
	}
	internal static class Portal
	{
		private const string Missing = "teleports take as long as in the game";

		internal static readonly FieldRef<Player, float> Timer = Reflect.Try(() => AccessTools.FieldRefAccess<Player, float>("m_teleportTimer"), "Player.m_teleportTimer", "teleports take as long as in the game");

		internal static readonly FieldRef<Player, Vector3> Target = Reflect.Try(() => AccessTools.FieldRefAccess<Player, Vector3>("m_teleportTargetPos"), "Player.m_teleportTargetPos", "teleports take as long as in the game");

		internal static readonly FieldRef<Player, bool> Distant = Reflect.Try(() => AccessTools.FieldRefAccess<Player, bool>("m_distantTeleport"), "Player.m_distantTeleport", "teleports take as long as in the game");

		internal static bool Ready
		{
			get
			{
				if (Timer != null && Target != null)
				{
					return Distant != null;
				}
				return false;
			}
		}

		internal static bool Lifted { get; set; }

		internal static float ArrivedAt { get; set; } = -100f;

		internal static bool Arriving()
		{
			Player localPlayer = Player.m_localPlayer;
			if (Ready && (Object)(object)localPlayer != (Object)null && ((Character)localPlayer).IsTeleporting() && Distant.Invoke(localPlayer))
			{
				return Timer.Invoke(localPlayer) > 2f;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "UpdateTeleport")]
	internal static class Patch_Player_UpdateTeleport_Portal
	{
		private static readonly FieldRef<ZNet, float> _periodicTimer = Reflect.Try(() => AccessTools.FieldRefAccess<ZNet, float>("m_periodicSendTimer"), "ZNet.m_periodicSendTimer", "a client tells the server about a teleport at the game's 2 s pace");

		private const float SendNow = 2f;

		private static readonly List<ZDO> _nearby = new List<ZDO>();

		private static readonly ArrivalTracker<ZDOID> _arrivals = new ArrivalTracker<ZDOID>();

		private static Player _player;

		private static bool _sent;

		private static bool _watching;

		private static bool Prepare()
		{
			return Portal.Ready;
		}

		private static void Prefix(Player __instance)
		{
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || !ShorterLoadingPlugin.IsApplied(typeof(Patch_Player_UpdateTeleport_Portal)))
			{
				return;
			}
			if (SteadyFrame.Transitions)
			{
				_player = null;
				return;
			}
			try
			{
				Before(__instance);
			}
			catch (Exception ex)
			{
				_player = null;
				ShorterLoadingPlugin.StandDown(typeof(Patch_Player_UpdateTeleport_Portal), "Portal waits", ex);
			}
		}

		private static void Postfix(Player __instance)
		{
			if ((Object)(object)_player == (Object)null || (Object)(object)__instance != (Object)(object)_player || !ShorterLoadingPlugin.IsApplied(typeof(Patch_Player_UpdateTeleport_Portal)))
			{
				return;
			}
			try
			{
				After(__instance);
			}
			catch (Exception ex)
			{
				_player = null;
				ShorterLoadingPlugin.StandDown(typeof(Patch_Player_UpdateTeleport_Portal), "Portal waits", ex);
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void Before(Player player)
		{
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			if (!((Character)player).IsTeleporting())
			{
				if ((Object)(object)_player == (Object)(object)player)
				{
					Portal.ArrivedAt = Time.time;
				}
				_player = null;
				return;
			}
			if ((Object)(object)_player != (Object)(object)player)
			{
				_player = player;
				_sent = false;
				_watching = false;
				Portal.Lifted = false;
			}
			Settings cfg = ShorterLoadingPlugin.Cfg;
			bool flag = cfg.Enabled.Value && cfg.PortalWaits.Value;
			if (!flag && !Patch_MapTeleport.Aimed(player))
			{
				return;
			}
			ref float reference = ref Portal.Timer.Invoke(player);
			if (flag && PortalRules.MoveNow(reference, LoadingScreen.Black()))
			{
				reference = 2f;
				return;
			}
			bool flag2 = Portal.Distant.Invoke(player);
			if (flag2 && !(reference <= 2f) && !(reference >= 8f))
			{
				Vector3 val = Portal.Target.Invoke(player);
				if (PortalRules.EndWaitNow(flag2, reference, ZNetScene.instance.IsAreaReady(val), ObjectsSettled(val, cfg)))
				{
					reference = 8f;
					Portal.Lifted = true;
				}
			}
		}

		private static bool ObjectsSettled(Vector3 target, Settings cfg)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			if (ZNet.instance.IsServer())
			{
				return true;
			}
			float time = Time.time;
			if (!_watching)
			{
				_watching = true;
				_arrivals.Reset(time);
			}
			_nearby.Clear();
			ZDOMan.instance.FindSectorObjects(ZoneSystem.GetZone(target), new SimulationDistance(1, 0, false), _nearby, (List<ZDO>)null);
			foreach (ZDO item in _nearby)
			{
				_arrivals.Observe(item.m_uid);
			}
			_arrivals.EndSample(time);
			int count = _nearby.Count;
			_nearby.Clear();
			bool num = SpawnRules.ClientSettled(count, _arrivals.Grew, time - _arrivals.Start, time - _arrivals.LastGrowth, cfg.ClientQuiet.Value, cfg.ClientFirstWait.Value);
			Patch_Teleport_Timing.ClientCheck(num);
			return num;
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void After(Player player)
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			Settings cfg = ShorterLoadingPlugin.Cfg;
			ZNet instance = ZNet.instance;
			if (!_sent && ((Character)player).IsTeleporting() && cfg.Enabled.Value && cfg.QuickPositionUpdate.Value && !((Object)(object)instance == (Object)null) && !instance.IsServer() && !(Portal.Timer.Invoke(player) <= 2f) && _periodicTimer != null)
			{
				_sent = true;
				instance.SetReferencePosition(((Component)player).transform.position);
				_periodicTimer.Invoke(instance) = 2f;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "TeleportTo")]
	internal static class Patch_Player_TeleportTo_Cooldown
	{
		private static readonly FieldRef<Player, float> _cooldown = Reflect.Try(() => AccessTools.FieldRefAccess<Player, float>("m_teleportCooldown"), "Player.m_teleportCooldown", "the time between teleports stays at 2 s");

		private static bool Prepare()
		{
			return _cooldown != null;
		}

		private static void Prefix(Player __instance)
		{
			Settings cfg = ShorterLoadingPlugin.Cfg;
			if (cfg.Enabled.Value && !SteadyFrame.Transitions && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				ref float reference = ref _cooldown.Invoke(__instance);
				if (PortalRules.CooldownDone(reference, cfg.PortalCooldown.Value))
				{
					reference = 2f;
				}
			}
		}
	}
	[HarmonyPatch(typeof(TeleportWorld), "UpdatePortal")]
	internal static class Patch_TeleportWorld_UpdatePortal_Preload
	{
		private delegate HMBuildData RequestTerrainFn(HeightmapBuilder builder, Vector3 center, int width, float scale, bool distantLod, WorldGenerator worldGen);

		private const string Missing = "portal destinations are not preloaded";

		private const float Range = 8f;

		private const float AfterArrival = 10f;

		private const float NearestFor = 0.6f;

		private static readonly FieldRef<TeleportWorld, ZNetView> _nview = Reflect.Try(() => AccessTools.FieldRefAccess<TeleportWorld, ZNetView>("m_nview"), "TeleportWorld.m_nview", "portal destinations are not preloaded");

		private static readonly FieldRef<ZoneSystem, SimulationDistance> _simulation = Reflect.Try(() => AccessTools.FieldRefAccess<ZoneSystem, SimulationDistance>("m_simulationDistance"), "ZoneSystem.m_simulationDistance", "portal destinations are not preloaded");

		private static readonly RequestTerrainFn _requestTerrain = Reflect.Try(() => AccessTools.MethodDelegate<RequestTerrainFn>(AccessTools.Method(typeof(HeightmapBuilder), "RequestTerrain", (Type[])null, (Type[])null), (object)null, true), "HeightmapBuilder.RequestTerrain", "portal destinations are not preloaded");

		private static TeleportWorld _nearest;

		private static float _nearestDistance;

		private static float _nearestAt = -100f;

		internal static Vector2s LastExit;

		internal static float LastAt = -100f;

		internal static float Since = -100f;

		internal static int Zones;

		internal static int Ready;

		private static bool Prepare()
		{
			if (_nview != null && _simulation != null)
			{
				return _requestTerrain != null;
			}
			return false;
		}

		private static void Postfix(TeleportWorld __instance)
		{
			if (!ShorterLoadingPlugin.IsApplied(typeof(Patch_TeleportWorld_UpdatePortal_Preload)))
			{
				return;
			}
			try
			{
				Preload(__instance);
			}
			catch (Exception ex)
			{
				ShorterLoadingPlugin.StandDown(typeof(Patch_TeleportWorld_UpdatePortal_Preload), "Portal preload", ex);
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void Preload(TeleportWorld portal)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: 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_010e: 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_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: 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_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a0: 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_0289: 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_02df: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: 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_01e3: 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_0218: 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_0224: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Unknown result type (might be due to invalid IL or missing references)
			Settings cfg = ShorterLoadingPlugin.Cfg;
			Player localPlayer = Player.m_localPlayer;
			if (!cfg.Enabled.Value || !cfg.PortalPreload.Value || SteadyFrame.Transitions || (Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsTeleporting() || Time.time - Portal.ArrivedAt < 10f)
			{
				return;
			}
			float num = Vector3.Distance(((Component)localPlayer).transform.position, ((Component)portal).transform.position);
			if (num > 8f || !Nearest(portal, num) || !((Humanoid)localPlayer).IsTeleportable(portal.m_allowAllItems) || (Object)(object)ZoneSystem.instance == (Object)null || ZoneSystem.instance.GetGlobalKey((GlobalKeys)32))
			{
				return;
			}
			ZNetView val = _nview.Invoke(portal);
			ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null);
			ZDO val3 = ((val2 == null) ? null : ZDOMan.instance.GetZDO(val2.GetConnectionZDOID((ConnectionType)1)));
			if (val3 == null || HeightmapBuilder.instance == null || WorldGenerator.instance == null)
			{
				return;
			}
			Vector3 val4 = val3.GetPosition() + val3.GetRotation() * Vector3.forward * portal.m_exitDistance;
			bool flag = cfg.TerrainCache.Value && !SteadyFrame.Terrain && ShorterLoadingPlugin.IsApplied(typeof(Patch_HeightmapBuilder_RequestTerrain_Cache));
			SimulationDistance val5 = _simulation.Invoke(ZoneSystem.instance);
			int num2 = ((!flag) ? 1 : ((SimulationDistance)(ref val5)).NearSimulationDistance);
			Heightmap componentInChildren = ZoneSystem.instance.m_zonePrefab.GetComponentInChildren<Heightmap>();
			Vector2s zone = ZoneSystem.GetZone(val4);
			bool value = cfg.LogTiming.Value;
			int num3 = 0;
			int num4 = 0;
			Vector2s val6 = default(Vector2s);
			for (int i = zone.y - num2; i <= zone.y + num2; i++)
			{
				for (int j = zone.x - num2; j <= zone.x + num2; j++)
				{
					((Vector2s)(ref val6))..ctor(j, i);
					if (num2 > 1 && !((SimulationDistance)(ref val5)).IsClassic && !ZoneSystem.instance.ZonesWithinRadius(zone, val6, num2, false))
					{
						continue;
					}
					if (value)
					{
						num3++;
					}
					if (ZoneSystem.instance.IsZoneLoaded(val6))
					{
						if (value)
						{
							num4++;
						}
						continue;
					}
					Vector3 zonePos = ZoneSystem.GetZonePos(val6);
					if (HeightmapBuilder.instance.IsTerrainReady(zonePos, componentInChildren.m_width, componentInChildren.m_scale, componentInChildren.IsDistantLod, WorldGenerator.instance))
					{
						if (value)
						{
							num4++;
						}
						if (flag)
						{
							_requestTerrain(HeightmapBuilder.instance, zonePos, componentInChildren.m_width, componentInChildren.m_scale, componentInChildren.IsDistantLod, WorldGenerator.instance);
						}
					}
				}
			}
			if (value)
			{
				float time = Time.time;
				if (!((Vector2s)(ref zone)).Equals(LastExit) || time - LastAt > 1f)
				{
					Since = time;
				}
				LastExit = zone;
				LastAt = time;
				Zones = num3;
				Ready = num4;
			}
		}

		private static bool Nearest(TeleportWorld portal, float distance)
		{
			float time = Time.time;
			if ((Object)(object)_nearest == (Object)null || (Object)(object)_nearest == (Object)(object)portal || time - _nearestAt > 0.6f || distance < _nearestDistance)
			{
				_nearest = portal;
				_nearestDistance = distance;
				_nearestAt = time;
				return true;
			}
			return false;
		}
	}
	internal enum WaitEnding
	{
		Unknown,
		Game,
		GameAreaLate,
		GameNoFloor,
		ShorterLoading,
		ShorterLoadingThenNoFloor,
		SteadyFrame,
		OtherMod
	}
	internal static class PortalRules
	{
		internal const float FadeGate = 2f;

		internal const float DistantGate = 8f;

		internal const float NoFloorFallback = 15f;

		internal const float GameCooldown = 2f;

		internal const float JumpDistance = 100f;

		private const float Early = 0.5f;

		internal static bool MoveNow(float timer, bool black)
		{
			return timer < 2f && black;
		}

		internal static bool EndWaitNow(bool distant, float timer, bool areaReady, bool objectsSettled)
		{
			return distant && timer > 2f && timer < 8f && areaReady && objectsSettled;
		}

		internal static bool CooldownDone(float cooldown, float setting)
		{
			if (cooldown < 2f)
			{
				return cooldown >= setting;
			}
			return false;
		}

		internal static bool IsJump(float distance)
		{
			return distance > 100f;
		}

		internal static WaitEnding Ending(float lastTimer, bool liftedByShorterLoading, bool steadyFrame, bool floor)
		{
			if (lastTimer < 0f)
			{
				return WaitEnding.Unknown;
			}
			bool flag = !floor && lastTimer + 0.5f >= 15f;
			if (liftedByShorterLoading)
			{
				if (!flag)
				{
					return WaitEnding.ShorterLoading;
				}
				return WaitEnding.ShorterLoadingThenNoFloor;
			}
			if (flag)
			{
				return WaitEnding.GameNoFloor;
			}
			if (lastTimer >= 8.5f)
			{
				return WaitEnding.GameAreaLate;
			}
			if (lastTimer + 0.5f >= 8f)
			{
				return WaitEnding.Game;
			}
			if (!steadyFrame)
			{
				return WaitEnding.OtherMod;
			}
			return WaitEnding.SteadyFrame;
		}
	}
	internal static class Rivers
	{
		internal static bool Failed;

		internal static bool GameWay { get; set; }

		internal static bool Active
		{
			get
			{
				Settings cfg = ShorterLoadingPlugin.Cfg;
				if (!GameWay && !Failed && cfg.Enabled.Value)
				{
					return cfg.FastRivers.Value;
				}
				return false;
			}
		}
	}
	[HarmonyPatch(typeof(WorldGenerator), "MergePoints")]
	internal static class Patch_WorldGenerator_MergePoints
	{
		private static bool Prefix(List<Vector2> points, float range, ref List<Vector2> __result)
		{
			if (!Rivers.Active)
			{
				return true;
			}
			try
			{
				__result = LakeMerge.Merge((IList<Vector2>)points, range, (Func<Vector2, float>)((Vector2 v) => v.x), (Func<Vector2, float>)((Vector2 v) => v.y), (Func<Vector2, Vector2, float>)Vector2.Distance, (Func<Vector2, Vector2, bool>)((Vector2 a, Vector2 b) => a == b), (Func<Vector2, Vector2, Vector2>)((Vector2 a, Vector2 b) => (a + b) * 0.5f));
			}
			catch (Exception ex)
			{
				Rivers.Failed = true;
				ShorterLoadingPlugin.Log.LogWarning((object)("Fast lake merge failed, merging the game's way: " + ex.Message));
				return true;
			}
			points.Clear();
			return false;
		}
	}
	[HarmonyPatch(typeof(WorldGenerator), "Pregenerate")]
	internal static class Patch_WorldGenerator_Pregenerate_Check
	{
		private sealed class Snapshot
		{
			private readonly List<Vector2> _lakeList;

			private readonly List<River> _riverList;

			private readonly List<River> _streamList;

			private readonly Dictionary<Vector2i, RiverPoint[]> _pointMap;

			internal Snapshot(WorldGenerator generator)
			{
				_lakeList = new List<Vector2>(_lakes.Invoke(generator) ?? new List<Vector2>());
				_riverList = new List<River>(_rivers.Invoke(generator));
				_streamList = new List<River>(_streams.Invoke(generator));
				_pointMap = new Dictionary<Vector2i, RiverPoint[]>(_points.Invoke(generator));
			}

			internal string Compare(Snapshot game, out bool same)
			{
				//IL_00be: Unknown result type (might be due to invalid IL or missing references)
				//IL_010f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0114: Unknown result type (might be due to invalid IL or missing references)
				//IL_011c: Unknown result type (might be due to invalid IL or missing references)
				int num = Differences(_lakeList, game._lakeList, (Vector2 a, Vector2 b) => a == b);
				int num2 = Differences(_riverList, game._riverList, Same);
				int num3 = Differences(_streamList, game._streamList, Same);
				int num4 = 0;
				int num5 = 0;
				foreach (KeyValuePair<Vector2i, RiverPoint[]> item in game._pointMap)
				{
					num5 += item.Value.Length;
					if (!_pointMap.TryGetValue(item.Key, out var value) || !Same(value, item.Value))
					{
						num4++;
					}
				}
				foreach (Vector2i key in _pointMap.Keys)
				{
					if (!game._pointMap.ContainsKey(key))
					{
						num4++;
					}
				}
				string text = $"{game._lakeList.Count} lakes, {game._riverList.Count} rivers, {game._streamList.Count} streams, " + $"{num5} river points in {game._pointMap.Count} cells";
				same = num + num2 + num3 + num4 == 0;
				if (!same)
				{
					return $"{num} lakes, {num2} rivers, {num3} streams and {num4} river point cells differ ({text})";
				}
				return "all match (" + text + ")";
			}

			private static int Differences<T>(List<T> ours, List<T> game, Func<T, T, bool> same)
			{
				int num = Math.Max(ours.Count, game.Count);
				int num2 = 0;
				for (int i = 0; i < num; i++)
				{
					if (i >= ours.Count || i >= game.Count || !same(ours[i], game[i]))
					{
						num2++;
					}
				}
				return num2;
			}

			private static bool Same(River a, River b)
			{
				//IL_0001: 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_0014: Unknown result type (might be due to invalid IL or missing references)
				//IL_001a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0027: Unknown result type (might be due to invalid IL or missing references)
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				if (a.p0 == b.p0 && a.p1 == b.p1 && a.center == b.center && a.widthMin.Equals(b.widthMin) && a.widthMax.Equals(b.widthMax) && a.curveWidth.Equals(b.curveWidth))
				{
					return a.curveWavelength.Equals(b.curveWavelength);
				}
				return false;
			}

			private static bool Same(RiverPoint[] a, RiverPoint[] b)
			{
				if (a.Length != b.Length)
				{
					return false;
				}
				for (int i = 0; i < a.Length; i++)
				{
					if (!a[i].p.x.Equals(b[i].p.x) || !a[i].p.y.Equals(b[i].p.y) || !a[i].w.Equals(b[i].w) || !a[i].w2.Equals(b[i].w2))
					{
						return false;
					}
				}
				return true;
			}
		}

		private static Action<WorldGenerator> _pregenerate;

		private static FieldRef<WorldGenerator, List<Vector2>> _lakes;

		private static FieldRef<WorldGenerator, List<River>> _rivers;

		private static FieldRef<WorldGenerator, List<River>> _streams;

		private static FieldRef<WorldGenerator, Dictionary<Vector2i, RiverPoint[]>> _points;

		private static FieldRef<WorldGenerator, Vector2i> _cachedGrid;

		private static bool _resolved;

		private static string _unavailable;

		private static bool _checking;

		private static bool Resolve()
		{
			if (!_resolved)
			{
				_resolved = true;
				try
				{
					_pregenerate = AccessTools.MethodDelegate<Action<WorldGenerator>>(AccessTools.Method(typeof(WorldGenerator), "Pregenerate", (Type[])null, (Type[])null), (object)null, true);
					_lakes = AccessTools.FieldRefAccess<WorldGenerator, List<Vector2>>("m_lakes");
					_rivers = AccessTools.FieldRefAccess<WorldGenerator, List<River>>("m_rivers");
					_streams = AccessTools.FieldRefAccess<WorldGenerator, List<River>>("m_streams");
					_points = AccessTools.FieldRefAccess<WorldGenerator, Dictionary<Vector2i, RiverPoint[]>>("m_riverPoints");
					_cachedGrid = AccessTools.FieldRefAccess<WorldGenerator, Vector2i>("m_cachedRiverGrid");
				}
				catch (Exception ex)
				{
					_unavailable = "WorldGenerator river fields unavailable (" + ex.GetType().Name + ")";
				}
			}
			return _unavailable == null;
		}

		private static bool Prefix(WorldGenerator __instance)
		{
			Settings cfg = ShorterLoadingPlugin.Cfg;
			if (_checking || !cfg.CheckRivers.Value)
			{
				return true;
			}
			if (!Resolve())
			{
				ShorterLoadingPlugin.Log.LogWarning((object)("River check NOT RUN: " + _unavailable));
				return true;
			}
			if (!Rivers.Active)
			{
				ShorterLoadingPlugin.Log.LogWarning((object)(Rivers.Failed ? "River check NOT RUN: 'Fast lake merge' failed earlier this session" : "River check NOT RUN: it needs ShorterLoading and 'Fast lake merge' on"));
				return true;
			}
			_checking = true;
			try
			{
				long timestamp = Stopwatch.GetTimestamp();
				_pregenerate(__instance);
				double num = Clock.Ms(timestamp);
				Snapshot snapshot = new Snapshot(__instance);
				Reset(__instance);
				Rivers.GameWay = true;
				long timestamp2 = Stopwatch.GetTimestamp();
				_pregenerate(__instance);
				double num2 = Clock.Ms(timestamp2);
				bool same;
				string text = snapshot.Compare(new Snapshot(__instance), out same);
				string text2 = "fast lake merge " + Clock.Seconds(num / 1000.0) + ", the game's way " + Clock.Seconds(num2 / 1000.0) + " (the game's rivers are used)";
				if (same)
				{
					ShorterLoadingPlugin.Log.LogInfo((object)("River check PASSED: " + text + "; " + text2));
				}
				else
				{
					ShorterLoadingPlugin.Log.LogWarning((object)("River check FAILED: " + text + "; " + text2));
				}
			}
			catch (Exception ex)
			{
				ShorterLoadingPlugin.Log.LogWarning((object)("River check NOT FINISHED: " + ex.Message + "; planning the rivers the game's way"));
				Reset(__instance);
				Rivers.GameWay = true;
				_pregenerate(__instance);
			}
			finally
			{
				Rivers.GameWay = false;
				_checking = false;
			}
			return false;
		}

		private static void Reset(WorldGenerator generator)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			generator.CleanCachedRiverData();
			_cachedGrid.Invoke(generator) = new Vector2i(-999999, -999999);
			_lakes.Invoke(generator) = null;
		}
	}
	[HarmonyPatch(typeof(WorldGenerator), "GetRiverWeight")]
	internal static class RiverLookup
	{
		private delegate void GetWeightFn(WorldGenerator generator, RiverPoint[] points, float wx, float wy, out float weight, out float width);

		private sealed class Cache
		{
			internal WorldGenerator Generator;

			internal Vector2i Grid;

			internal RiverPoint[] Cell;
		}

		private static readonly GetWeightFn _getWeight = Reflect.Try(() => AccessTools.MethodDelegate<GetWeightFn>(AccessTools.Method(typeof(WorldGenerator), "GetWeight", (Type[])null, (Type[])null), (object)null, true), "WorldGenerator.GetWeight", "worker threads share the game's river cache");

		private static readonly FieldRef<WorldGenerator, Dictionary<Vector2i, RiverPoint[]>> _points = Reflect.Try(() => AccessTools.FieldRefAccess<WorldGenerator, Dictionary<Vector2i, RiverPoint[]>>("m_riverPoints"), "WorldGenerator.m_riverPoints", "worker threads share the game's river cache");

		private static int s_users;

		[ThreadStatic]
		private static Cache t_cache;

		private static bool Prepare()
		{
			if (_getWeight != null)
			{
				return _points != null;
			}
			return false;
		}

		internal static void Enter()
		{
			t_cache = new Cache();
			Interlocked.Increment(ref s_users);
		}

		internal static void Exit()
		{
			t_cache = null;
			Interlocked.Decrement(ref s_users);
		}

		private static bool Prefix(WorldGenerator __instance, float wx, float wy, out float weight, out float width)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: 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)
			weight = 0f;
			width = 0f;
			Cache cache;
			if (Volatile.Read(in s_users) == 0 || (cache = t_cache) == null)
			{
				return true;
			}
			Vector2i riverGrid = __instance.GetRiverGrid(wx, wy);
			if (cache.Generator != __instance || !(riverGrid == cache.Grid))
			{
				_points.Invoke(__instance).TryGetValue(riverGrid, out cache.Cell);
				cache.Grid = riverGrid;
				cache.Generator = __instance;
			}
			if (cache.Cell != null)
			{
				_getWeight(__instance, cache.Cell, wx, wy, out weight, out width);
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(ZDOMan), "SendZDOs")]
	internal static class Patch_ZDOMan_SendZDOs_Boost
	{
		private const int GameBudget = 10240;

		private const int Factor = 4;

		private const float BoostSeconds = 10f;

		private static readonly Type _zdoPeer = AccessTools.Inner(typeof(ZDOMan), "ZDOPeer");

		private static readonly FieldRef<object, ZNetPeer> _peer = ((_zdoPeer == null) ? null : Reflect.Try(() => AccessTools.FieldRefAccess<ZNetPeer>(_zdoPeer, "m_peer"), "ZDOMan.ZDOPeer.m_peer", "objects are sent at the game's rate after a jump"));

		private static readonly Dictionary<long, float> _boostUntil = new Dictionary<long, float>();

		private static readonly List<long> _expired = new List<long>();

		private static bool Prepare()
		{
			return _peer != null;
		}

		[HarmonyPriority(200)]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Expected O, but got Unknown
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			List<int> list2 = new List<int>();
			for (int i = 0; i < list.Count; i++)
			{
				if (CodeInstructionExtensions.LoadsConstant(list[i], 10240L))
				{
					list2.Add(i);
				}
			}
			if (list2.Count != 2)
			{
				ShorterLoadingPlugin.Log.LogWarning((object)$"Server send boost: expected two {10240}-byte budgets in ZDOMan.SendZDOs, found {list2.Count}; objects are sent at the game's rate");
				return list;
			}
			MethodInfo methodInfo = AccessTools.Method(typeof(Patch_ZDOMan_SendZDOs_Boost), "Budget", (Type[])null, (Type[])null);
			for (int num = list2.Count - 1; num >= 0; num--)
			{
				int index = list2[num];
				CodeInstruction item = CodeInstructionExtensions.MoveLabelsFrom(new CodeInstruction(OpCodes.Ldarg_1, (object)null), list[index]);
				list[index] = new CodeInstruction(OpCodes.Call, (object)methodInfo);
				list.Insert(index, item);
			}
			return list;
		}

		private static int Budget(object zdoPeer)
		{
			try
			{
				Settings cfg = ShorterLoadingPlugin.Cfg;
				if (_boostUntil.Count == 0 || !cfg.Enabled.Value || !cfg.ServerSendBoost.Value || SteadyFrame.Transitions)
				{
					return 10240;
				}
				ZNetPeer val = _peer.Invoke(zdoPeer);
				if (val != null && _boostUntil.TryGetValue(val.m_uid, out var value))
				{
					if (Time.realtimeSinceStartup < value)
					{
						return 40960;
					}
					_boostUntil.Remove(val.m_uid);
				}
			}
			catch (Exception)
			{
			}
			return 10240;
		}

		internal static void Jumped(ZNetPeer peer)
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			_expired.Clear();
			foreach (KeyValuePair<long, float> item in _boostUntil)
			{
				if (item.Value <= realtimeSinceStartup)
				{
					_expired.Add(item.Key);
				}
			}
			foreach (long item2 in _expired)
			{
				_boostUntil.Remove(item2);
			}
			_boostUntil[peer.m_uid] = realtimeSinceStartup + 10f;
		}
	}
	[HarmonyPatch(typeof(ZNet), "RPC_ServerSyncedPlayerData")]
	internal static class Patch_ZNet_RPC_ServerSyncedPlayerData_Jump
	{
		private static readonly Func<ZNet, ZRpc, ZNetPeer> _getPeer = Reflect.Try(() => AccessTools.MethodDelegate<Func<ZNet, ZRpc, ZNetPeer>>(AccessTools.Method(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null), (object)null, true), "ZNet.GetPeer", "objects are sent at the game's rate after a jump");

		private static Vector3 _from;

		private static bool Prepare()
		{
			return _getPeer != null;
		}

		private static void Prefix(ZNet __instance, ZRpc rpc, out ZNetPeer __state)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			__state = null;
			if (__instance.IsServer() && !SteadyFrame.Transitions && ShorterLoadingPlugin.IsApplied(typeof(Patch_ZDOMan_SendZDOs_Boost)))
			{
				__state = _getPeer(__instance, rpc);
				if (__state != null)
				{
					_from = __state.m_refPos;
				}
			}
		}

		private static void Postfix(ZNetPeer __state)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			if (__state != null && PortalRules.IsJump(Vector3.Distance(_from, __state.m_refPos)))
			{
				Patch_ZDOMan_SendZDOs_Boost.Jumped(__state);
			}
		}
	}
	internal sealed class Settings
	{
		public readonly ConfigEntry<bool> Enabled;

		public readonly ConfigEntry<bool> SkipFixedWait;

		public readonly ConfigEntry<float> ClientQuiet;

		public readonly ConfigEntry<float> ClientFirstWait;

		public readonly ConfigEntry<float> DeathDelay;

		public readonly ConfigEntry<float> ZoneRushBudget;

		public readonly ConfigEntry<bool> MapTeleport;

		public readonly ConfigEntry<bool> ObjectRush;

		public readonly ConfigEntry<float> ObjectRushBudget;

		public readonly ConfigEntry<int> ObjectRushMax;

		public readonly ConfigEntry<bool> LoadLessFirst;

		public readonly ConfigEntry<int> LoadLessRadius;

		public readonly ConfigEntry<bool> SkipSplash;

		public readonly ConfigEntry<float> MenuFadeInTime;

		public readonly ConfigEntry<float> MenuFadeTime;

		public readonly ConfigEntry<bool> FastBiomeMap;

		public readonly ConfigEntry<bool> EarlyBiomeMap;

		public readonly ConfigEntry<bool> FastRivers;

		public readonly ConfigEntry<bool> FasterSleep;

		public readonly ConfigEntry<float> DreamReadingTime;

		public readonly ConfigEntry<float> SleepFadeTime;

		public readonly ConfigEntry<bool> AlwaysDream;

		public readonly ConfigEntry<bool> LoadingHangFix;

		public readonly ConfigEntry<bool> NoBuilderPause;

		public readonly ConfigEntry<bool> TerrainCache;

		public readonly ConfigEntry<bool> DungeonSpread;

		public readonly ConfigEntry<bool> GrassBudget;

		public readonly ConfigEntry<bool> PortalWaits;

		public readonly ConfigEntry<float> PortalFadeTime;

		public readonly ConfigEntry<float> PortalCooldown;

		public readonly ConfigEntry<bool> PortalRush;

		public readonly ConfigEntry<bool> PortalPreload;

		public readonly ConfigEntry<bool> QuickPositionUpdate;

		public readonly ConfigEntry<bool> ServerSendBoost;

		public readonly ConfigEntry<bool> LogTiming;

		public readonly ConfigEntry<bool> CheckRivers;

		public readonly ConfigEntry<bool> CheckBiomeMap;

		private static readonly List<(ConfigurationManagerAttributes Attributes, string[] Modules)> _overlap = new List<(ConfigurationManagerAttributes, string[])>();

		public Settings(ConfigFile config)
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Expected O, but got Unknown
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Expected O, but got Unknown
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Expected O, but got Unknown
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Expected O, but got Unknown
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Expected O, but got Unknown
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Expected O, but got Unknown
			//IL_0266: Unknown result type (might be due to invalid IL or missing references)
			//IL_0270: Expected O, but got Unknown
			//IL_029f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a9: Expected O, but got Unknown
			//IL_0348: Unknown result type (might be due to invalid IL or missing references)
			//IL_0352: Expected O, but got Unknown
			//IL_0381: Unknown result type (might be due to invalid IL or missing references)
			//IL_038b: Expected O, but got Unknown
			bool saveOnConfigSet = config.SaveOnConfigSet;
			config.SaveOnConfigSet = false;
			Enabled = config.Bind<bool>("1 - General", "Enabled", true, "Enable ShorterLoading. Applies at once.");
			SkipFixedWait = config.Bind<bool>("2 - Spawn", "Skip fixed wait", true, "The game waits at least 8 seconds before placing you at your logout point (entering a world) or at your bed (after dying), even when the area is already loaded. Spawn as soon as the game's own checks pass instead: the area around the spawn point is created, there is ground, and your bed is there. If a check is not met, the game's 8 seconds apply as before.");
			ClientQuiet = config.Bind<float>("2 - Spawn", "Client quiet time", 1.5f, new ConfigDescription("Multiplayer client only: seconds without new objects arriving around the spawn point before it counts as complete. A client always waits at least 2.5 s, because it tells the host where it is only every 2 s. The host or single player has all objects at hand and does not wait.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 8f), Array.Empty<object>()));
			ClientFirstWait = config.Bind<float>("2 - Spawn", "Client first wait", 3.5f, new ConfigDescription("Multiplayer client only: seconds to wait when the objects around the spawn point were all there from the start (none arrived while waiting), in case the host has not started sending yet. The client tells the host its new position only every 2 s, so this cannot be shorter than 2.5 s.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2.5f, 8f), Array.Empty<object>()));
			DeathDelay = config.Bind<float>("2 - Spawn", "Death delay", 3f, new ConfigDescription("Seconds between dying and the respawn starting; the screen fades to black over this time. The game uses 10; set 10 to keep the game's. Lower values only shorten it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>()));
			ZoneRushBudget = config.Bind<float>("2 - Spawn", "Zone rush budget", 50f, new ConfigDescription("Milliseconds per frame for creating the ground around the spawn point while the loading screen is up. The game creates one zone every 0.1 s and places no objects until all zones around you exist (9 to 137 zones depending on the simulation distance setting). This creates every zone whose terrain is ready instead. 0 keeps the game's pace. Frames get longer, but only while the loading screen is fully black (not during a new character's intro).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			MapTeleport = config.Bind<bool>("2 - Spawn", "Map teleport", true, "With devcommands, Ctrl + middle-click on the large map teleports you there like a portal, with the loading swirl. When that area is not loaded yet, the game aims below the ground, finds no floor and keeps the swirl up for 15 seconds. Aim at the ground once it has loaded instead, and end the teleport as soon as the area is ready rather than after at least 8 seconds (SteadyFrame does the latter itself when installed).");
			ObjectRush = config.Bind<bool>("3 - Objects", "Object rush", true, "Before you spawn, the game creates the objects around you (building pieces, items, trees) at most 100 per tick and 30 ticks a second, even when your PC could do far more. Create as many as fit in the time budget below instead, every frame, only while the loading screen is fully black (not during a new character's intro).");
			ObjectRushBudget = config.Bind<float>("3 - Objects", "Object rush budget", 50f, new ConfigDescription("Milliseconds per tick for creating objects during the object rush. Higher spawns sooner with longer frames behind the black screen.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 200f), Array.Empty<object>()));
			ObjectRushMax = config.Bind<int>("3 - Objects", "Object rush max per tick", 5000, new ConfigDescription("Most objects the object rush creates in one tick, whatever the budget allows. The game's own limit is 100.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(100, 50000), Array.Empty<object>()));
			LoadLessFirst = config.Bind<bool>("3 - Objects", "Load less first", true, "Before you spawn, create only the objects near the spawn point (see the radius below); the rest of the area loads after you appear, at the game's in-game pace, so distant parts of a large base may build up around you over several seconds. Without it the game creates every object of the area first.");
			LoadLessRadius = config.Bind<int>("3 - Objects", "Load less first radius", 1, new ConfigDescription("Zones (64 m each) around the spawn point whose objects are created before you spawn when 'Load less first' is on. 1 is the 3x3 zones the game's spawn check waits for.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 4), Array.Empty<object>()));
			SkipSplash = config.Bind<bool>("4 - World loading", "Skip splash screens", true, "When the game starts, it shows the Coffee Stain and Iron Gate logos for 2 seconds each while the main menu loads. Skip them; the game logo and loading bar show until the menu is ready. Applies at the next game start.");
			MenuFadeInTime = config.Bind<float>("4 - World loading", "Main menu fade-in time", 0.3f, new ConfigDescription("Seconds for the main menu to fade in when it first appears. Shorter than the game's fade only; a longer value keeps the game's.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 3f), Array.Empty<object>()));
			MenuFadeTime = config.Bind<float>("4 - World loading", "Menu fade time", 0.1f, new ConfigDescription("After you press Start (or join a server), the menu fades out to its 'Loading' text over 1.5 seconds before loading begins. Play the fade this fast instead (seconds); 1.5 keeps the game's.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1.5f), Array.Empty<object>()));
			FastBiomeMap = config.Bind<bool>("4 - World loading", "Biome map on all cores", true, "On every world load the game computes a map of biomes and heights (2048 x 2048 points) on one CPU core, which takes several seconds. Compute the same map on all cores instead. If anything goes wrong, the game computes it its own way.");
			EarlyBiomeMap = config.Bind<bool>("4 - World loading", "Biome map early", true, "Start the biome map on all cores as soon as the rivers are planned, while the game reads the world file instead of after it. Needs 'Biome map on all cores'.");
			FastRivers = config.Bind<bool>("4 - World loading", "Fast lake merge", true, "On every world load the game plans all lakes, rivers and streams. Most of that time goes into merging the low points of the map (mostly sea) into lakes, where each step searches every remaining point. Search only the nearby points instead; the merge picks the same points, so the lakes and rivers are the same.");
			FasterSleep = config.Bind<bool>("5 - Sleep", "Faster sleep", true, "The game skips the night in 12 seconds and checks only every 2 seconds whether everyone is in bed and whether the night is over. Skip it in 5.5 seconds and check every 0.25 seconds instead. Dreams still show, and a dream cutscene still plays to the end. Takes effect when the host (or single player) has ShorterLoading.");
			DreamReadingTime = config.Bind<float>("5 - Sleep", "Dream reading time", 3f, new ConfigDescription("When a dream text shows, keep sleeping until it has been readable for this many seconds. 0 wakes you as soon as possible. Applies on the host (or single player).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			SleepFadeTime = config.Bind<float>("5 - Sleep", "Sleep fade time", 0.5f, new ConfigDescription("Seconds for the screen to fade to black when you fall asleep (the game's is 3) and back in when you wake (the game's is 1). Shorter than the game's fades only. Needs 'Faster sleep'.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 3f), Array.Empty<object>()));
			AlwaysDream = config.Bind<bool>("5 - Sleep", "Always dream", false, "For testing the dream reading time: the game shows a dream text on only some nights, by chance. Show one every night instead (a dream cutscene still plays when one is due). Affects the dream on this PC; the reading time is kept by the host (or single player).");
			LoadingHangFix = config.Bind<bool>("6 - Portals and streaming", "Loading hang fix", true, OverlapDescription(Only("Housekeeping"), "A game bug can keep the loading screen up for ever: an object near the spawn point was destroyed without the game being told, so it waits for that object. Count the area as ready when such objects are all that is missing."));
			NoBuilderPause = config.Bind<bool>("6 - Portals and streaming", "No terrain builder pause", true, OverlapDescription(Only("Terrain"), "The game's terrain builder pauses 10 ms after every zone, even when more are waiting. Skip the pause while zones are waiting, so the ground is ready sooner."));
			TerrainCache = config.Bind<bool>("6 - Portals and streaming", "Terrain cache", true, OverlapDescription(Only("Terrain"), "The game keeps the ground of only the last 16 zones it built. Keep 512 for the session (at most about 45 MB), so respawning or walking back where you were is faster."));
			DungeonSpread = config.Bind<bool>("6 - Portals and streaming", "Dungeon spread", true, OverlapDescription(Only("Object streaming"), "A dungeon or camp loading near you places all its rooms in one frame. Place them over several frames instead, at most 4 ms a frame. Behind a loading screen they are placed at once."));
			GrassBudget = config.Bind<bool>("6 - Portals and streaming", "Grass rebuild budget", true, OverlapDescription(Only("Grass"), "When the ground changes nearby, the game rebuilds all grass within grass distance in one frame. Rebuild it over several frames instead, nearest first, at most 3 ms a frame. Behind a loading screen it is rebuilt at once."));
			PortalWaits = config.Bind<bool>("6 - Portals and streaming", "Portal waits", true, OverlapDescription(Only("Transitions"), "A portal trip moves you only after a fixed 2 seconds and ends no sooner than 8 seconds in, even when everything at the exit is ready. Move you as soon as the screen is black, and end the trip as soon as the area at the exit is ready and there is floor (the game's own checks). On someone else's server it also waits until the server's objects at the exit have stopped arriving ('Client quiet time', 'Client first wait'), so you never land before a floor built there. Dungeon entrances also move you as soon as the screen is black."));
			PortalFadeTime = config.Bind<float>("6 - Portals and streaming", "Portal fade time", 0.5f, OverlapDescription(Only("Transitions"), "Seconds for the screen to fade to black when a teleport starts and back in when you arrive (the game's is 1). Shorter than the game's only.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f)));
			PortalCooldown = config.Bind<float>("6 - Portals and streaming", "Portal cooldown", 1f, OverlapDescription(Only("Transitions"), "Seconds after a teleport before the next one is allowed (the game's is 2), e.g. to go straight on through the next portal.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 2f)));
			PortalRush = config.Bind<bool>("6 - Portals and streaming", "Portal loading rush", true, OverlapDescription(new string[3] { "Transitions", "Object streaming", "Terrain" }, "While a portal trip loads the area at the exit behind the black screen, create its ground and objects as fast as when you spawn: the zone rush and object rush budgets (sections 2 and 3) and 'Load less first' apply."));
			PortalPreload = config.Bind<bool>("6 - Portals and streaming", "Portal preload", true, OverlapDescription(Only("Transitions"), "When you stand near a connected portal you can use, have the ground at the other end built in the background, so a trip through it waits less. With 'Terrain cache' on, all the ground the trip waits for is built and kept; without it, only the nearest zones. Only the nearest portal of a group does this, and not in the first 10 seconds after you arrive somewhere."));
			QuickPositionUpdate = config.Bind<bool>("6 - Portals and streaming", "Quick position update", true, OverlapDescription(Only("Transitions"), "Multiplayer client: the game tells the server where you are only every 2 seconds, and the server sends the objects around that spot. Tell it at once when a teleport moves you, so the objects at the exit start arriving up to 2 seconds sooner. Works with any server."));
			ServerSendBoost = config.Bind<bool>("6 - Portals and streaming", "Server send boost", true, OverlapDescription(Only("Transitions"), "When you host or run a server: for 10 seconds after a player jumps far (a portal trip, a teleport, joining), send that player the objects around them up to 4 times as fast as the game does. Only the host's or server's setting counts."));
			LogTiming = config.Bind<bool>("7 - Diagnostics", "Log timing", false, "For finding out where the time goes, e.g. to include in a bug report. Writes a few lines per spawn (entering a world, or respawning after death): how long the loading screen took and what it waited for (the ground, the objects around you, your bed), whether the game's fixed wait was skipped, and how the rest of a large base loaded in after you appeared. On entering a world it also lists where the time went before the loading screen (scene load, rivers, world file, biome map). One line per night's sleep (lying down to waking, the night skip, the dream) and per teleport: portals, dungeon entrances and the devcommands map teleport (start to arrival, when you were moved and when the area was ready). Switching it on or off applies at once; nothing of it runs while off. Results go to BepInEx/LogOutput.log in your mod manager profile's BepInEx folder.");
			CheckRivers = config.Bind<bool>("7 - Diagnostics", "Check rivers", false, "THE WORLD WILL LOAD SLOWLY WHILE THIS IS ON. Checks that 'Fast lake merge' gives exactly the game's lakes and rivers on this world: plans them both ways on every world load, compares every lake, river and river point, and logs whether the check PASSED or FAILED, with both times. The game's own rivers are the ones used. Needs 'Fast lake merge' on. Turn it off again once it has passed. Results go to BepInEx/LogOutput.log in your mod manager profile's BepInEx folder.");
			CheckBiomeMap = config.Bind<bool>("7 - Diagnostics", "Check biome map", false, "THE WORLD WILL LOAD SLOWLY WHILE THIS IS ON. Checks that 'Biome map on all cores' gives exactly the game's biome map on this world: computes it both ways on every world load, compares all 4 million points, and logs whether the check PASSED or FAILED, with both times. The game's own map is the one used, and 'Biome map early' is not. Needs 'Biome map on all cores' on. Turn it off again once it has passed. Results go to BepInEx/LogOutput.log in your mod manager profile's BepInEx folder.");
			config.Save();
			config.SaveOnConfigSet = saveOnConfigSet;
		}

		private static string[] Only(string module)
		{
			return new string[1] { module };
		}

		private static ConfigDescription OverlapDescription(string[] modules, string text, AcceptableValueBase range = null)
		{
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Expected O, but got Unknown
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected O, but got Unknown
			string text2 = "'" + string.Join("' or '", modules) + "'";
			string text3 = ((modules.Length == 1) ? ("its " + text2 + " module") : ("its " + text2 + " modules"));
			if (Array.Exists(modules, SteadyFrame.ModuleOn))
			{
				ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes();
				_overlap.Add((configurationManagerAttributes, modules));
				Refresh(configurationManagerAttributes, modules);
				return new ConfigDescription("While SteadyFrame is switched on, it does this (" + text3 + ") and this setting has no effect (shown read-only). Switch SteadyFrame off in its settings and ShorterLoading does it at once. " + text, range, new object[1] { configurationManagerAttributes });
			}
			return new ConfigDescription(text + " Turns itself off while SteadyFrame does the same (SteadyFrame switched on, with " + text3 + " on), whatever this is set to.", range, Array.Empty<object>());
		}

		private static void Refresh(ConfigurationManagerAttributes attributes, string[] modules)
		{
			attributes.ReadOnly = Array.Exists(modules, SteadyFrame.Runs);
		}

		internal static void RefreshOverlap()
		{
			foreach (var (attributes, modules) in _overlap)
			{
				Refresh(attributes, modules);
			}
		}
	}
	[BepInPlugin("davidsv.shorterloading", "ShorterLoading", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class ShorterLoadingPlugin : BaseUnityPlugin
	{
		public const string Guid = "davidsv.shorterloading";

		public const string Name = "ShorterLoading";

		public const string Version = "1.0.0";

		internal static ManualLogSource Log;

		internal static Settings Cfg;

		private static readonly HashSet<Type> Applied = new HashSet<Type>();

		private Harmony _harmony;

		internal static bool IsApplied(Type patch)
		{
			return Applied.Contains(patch);
		}

		internal static void StandDown(Type patch, string what, Exception ex)
		{
			Applied.Remove(patch);
			Log.LogError((object)(what + " stands down: " + ex.Message));
		}

		private void Awake()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Cfg = new Settings(((BaseUnityPlugin)this).Config);
			_harmony = new Harmony("davidsv.shorterloading");
			if (SteadyFrame.Installed)
			{
				SteadyFrame.Watch();
				SteadyFrame.Switched += Settings.RefreshOverlap;
				SteadyFrame.Switched += delegate
				{
					if (SteadyFrame.Terrain)
					{
						TerrainCache.Clear();
					}
				};
			}
			Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(typeof(ShorterLoadingPlugin).Assembly);
			foreach (Type type in typesFromAssembly)
			{
				if (!type.IsDefined(typeof(HarmonyPatch), inherit: false) || type.IsDefined(typeof(TimingPatchAttribute), inherit: false))
				{
					continue;
				}
				try
				{
					List<MethodInfo> list = _harmony.CreateClassProcessor(type).Patch();
					if (list != null && list.Count > 0)
					{
						Applied.Add(type);
					}
				}
				catch (Exception ex)
				{
					Log.LogError((object)(type.Name + " could not be applied, that part stands down: " + ex.Message));
				}
			}
			TimingPatches.Init(Cfg);
		}
	}
	internal static class Sleep
	{
		private const string Missing = "sleep takes as long as in the game";

		internal static readonly FieldRef<EnvMan, double> SkipSpeed = Reflect.Try(() => AccessTools.FieldRefAccess<EnvMan, double>("m_timeSkipSpeed"), "EnvMan.m_timeSkipSpeed", "sleep takes as long as in the game");

		internal static readonly FieldRef<EnvMan, double> SkipTo = Reflect.Try(() => AccessTools.FieldRefAccess<EnvMan, double>("m_skipToTime"), "EnvMan.m_skipToTime", "sleep takes as long as in the game");

		internal static bool Ready
		{
			get
			{
				if (SkipSpeed != null)
				{
					return SkipTo != null;
				}
				return false;
			}
		}

		internal stati