Decompiled source of PieceBatcher v1.2.1

PieceBatcher.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Entities;
using Jotunn.Managers;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.LowLevel;
using UnityEngine.Rendering;

[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("PieceBatcher")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.2.1.0")]
[assembly: AssemblyInformationalVersion("1.2.1")]
[assembly: AssemblyProduct("PieceBatcher")]
[assembly: AssemblyTitle("PieceBatcher")]
[assembly: AssemblyVersion("1.2.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace PieceBatcher
{
	internal struct GroupKey : IEquatable<GroupKey>
	{
		public int MatId;

		public Material Mat;

		public int Layer;

		public uint RenderingLayerMask;

		public ShadowCastingMode Cast;

		public bool Receive;

		public LightProbeUsage Lp;

		public ReflectionProbeUsage Rp;

		public MotionVectorGenerationMode Mv;

		public bool Occl;

		public int Signature;

		public static GroupKey Of(MeshRenderer r, Material m, int signature)
		{
			//IL_003f: 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_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			return new GroupKey
			{
				MatId = ((Object)m).GetInstanceID(),
				Mat = m,
				Layer = ((Component)r).gameObject.layer,
				RenderingLayerMask = ((Renderer)r).renderingLayerMask,
				Cast = ((Renderer)r).shadowCastingMode,
				Receive = ((Renderer)r).receiveShadows,
				Lp = ((Renderer)r).lightProbeUsage,
				Rp = ((Renderer)r).reflectionProbeUsage,
				Mv = ((Renderer)r).motionVectorGenerationMode,
				Occl = ((Renderer)r).allowOcclusionWhenDynamic,
				Signature = signature
			};
		}

		public bool Equals(GroupKey o)
		{
			//IL_002b: 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_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			if (MatId == o.MatId && Layer == o.Layer && RenderingLayerMask == o.RenderingLayerMask && Cast == o.Cast && Receive == o.Receive && Lp == o.Lp && Rp == o.Rp && Mv == o.Mv && Occl == o.Occl)
			{
				return Signature == o.Signature;
			}
			return false;
		}

		public override bool Equals(object obj)
		{
			if (obj is GroupKey o)
			{
				return Equals(o);
			}
			return false;
		}

		public override int GetHashCode()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: 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_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: 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_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected I4, but got Unknown
			return ((((((((MatId * 31 + Layer) * 31 + (int)RenderingLayerMask) * 31 + Cast) * 31 + Receive) * 31 + Lp) * 31 + Rp) * 31 + Mv) * 31 + Occl) * 31 + Signature;
		}
	}
	internal sealed class PartPlan
	{
		public Entry E;

		public MeshRenderer R;

		public MeshData D;

		public Matrix4x4 M;

		public Matrix4x4 Nm;

		public bool Flip;

		public GroupKey[] Keys;

		public LODGroup Lod;

		public bool Accepted;
	}
	internal sealed class Entry
	{
		public WearNTear Wnt;

		public GameObject Root;

		public Transform Tr;

		public Vector3 Pos;

		public Cell Cell;

		public string Prefab;

		public bool Combined;

		public bool Highlighted;

		public bool MaterialTouched;

		public bool Ineligible;

		public bool Checked;

		public bool Moved;

		public int Gen;

		public int Index;

		public List<PartPlan> Parts;

		public readonly List<Renderer> Hidden = new List<Renderer>();

		public readonly List<GameObject> LodExtra = new List<GameObject>();

		public readonly List<Group> Groups = new List<Group>();

		public bool Excluded
		{
			get
			{
				if (!Highlighted && !MaterialTouched && !Ineligible)
				{
					return Moved;
				}
				return true;
			}
		}
	}
	internal struct RangeRef
	{
		public Entry E;

		public int Start;

		public int Count;
	}
	internal sealed class Group
	{
		public GroupKey Key;

		public Cell Cell;

		public GameObject Go;

		public Mesh Mesh;

		public int[] Master;

		public int VertexCount;

		public readonly List<RangeRef> Ranges = new List<RangeRef>();
	}
	internal sealed class Cell
	{
		public long Key;

		public Vector3 Origin;

		public readonly List<Entry> Entries = new List<Entry>();

		public readonly List<Group> Groups = new List<Group>();

		public bool Dirty;

		public float DirtyTime;

		public int Version;

		public RebuildJob Job;
	}
	internal sealed class GroupBuilder
	{
		public GroupKey Key;

		public MeshData First;

		public int Parts;

		public readonly List<Vector3> Pos = new List<Vector3>();

		public readonly List<Vector3> Nrm = new List<Vector3>();

		public readonly List<Vector4> Tan = new List<Vector4>();

		public readonly List<Color32> Col = new List<Color32>();

		public readonly List<Vector4>[] Uv = new List<Vector4>[4]
		{
			new List<Vector4>(),
			new List<Vector4>(),
			new List<Vector4>(),
			new List<Vector4>()
		};

		public readonly List<int> Idx = new List<int>();

		public readonly List<RangeRef> Ranges = new List<RangeRef>();

		public int[] Master;

		public void Add(PartPlan p, int sub)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: 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_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			MeshData d = p.D;
			if (First == null)
			{
				First = d;
			}
			Matrix4x4 m = p.M;
			int count = Pos.Count;
			int count2 = Idx.Count;
			Vector3[] pos = d.Pos;
			for (int i = 0; i < pos.Length; i++)
			{
				Pos.Add(((Matrix4x4)(ref m)).MultiplyPoint3x4(pos[i]));
			}
			Vector3 val;
			if (d.Nrm != null)
			{
				Matrix4x4 nm = p.Nm;
				Vector3[] nrm = d.Nrm;
				for (int j = 0; j < nrm.Length; j++)
				{
					List<Vector3> nrm2 = Nrm;
					val = ((Matrix4x4)(ref nm)).MultiplyVector(nrm[j]);
					nrm2.Add(((Vector3)(ref val)).normalized);
				}
			}
			if (d.Tan != null)
			{
				Vector4[] tan = d.Tan;
				foreach (Vector4 val2 in tan)
				{
					val = ((Matrix4x4)(ref m)).MultiplyVector(new Vector3(val2.x, val2.y, val2.z));
					Vector3 normalized = ((Vector3)(ref val)).normalized;
					Tan.Add(new Vector4(normalized.x, normalized.y, normalized.z, p.Flip ? (0f - val2.w) : val2.w));
				}
			}
			if (d.Col != null)
			{
				Col.AddRange(d.Col);
			}
			for (int l = 0; l < 4; l++)
			{
				if (d.Uv[l] != null)
				{
					Uv[l].AddRange(d.Uv[l]);
				}
			}
			int[] array = d.Sub[sub];
			if (!p.Flip)
			{
				for (int n = 0; n < array.Length; n++)
				{
					Idx.Add(array[n] + count);
				}
			}
			else
			{
				for (int num = 0; num + 2 < array.Length; num += 3)
				{
					Idx.Add(array[num] + count);
					Idx.Add(array[num + 2] + count);
					Idx.Add(array[num + 1] + count);
				}
			}
			Ranges.Add(new RangeRef
			{
				E = p.E,
				Start = count2,
				Count = Idx.Count - count2
			});
			Parts++;
		}
	}
	internal sealed class RebuildJob
	{
		public Cell Cell;

		public int Version;

		public List<PartPlan> Parts = new List<PartPlan>();

		public Dictionary<Entry, HashSet<Renderer>> ToHide = new Dictionary<Entry, HashSet<Renderer>>();

		public Dictionary<Entry, HashSet<Renderer>> ToHideExtra = new Dictionary<Entry, HashSet<Renderer>>();

		public List<GroupBuilder> Builders = new List<GroupBuilder>();

		public int MaxVertices;

		public volatile bool Done;

		public Exception Error;

		public double WorkerMs;

		public double CollectMs;
	}
	internal sealed class Watcher : MonoBehaviour
	{
		public Entry E;

		public int Gen;

		private void OnDisable()
		{
			if (!Batcher.Quitting && !Batcher.Clearing && E != null && E.Combined && E.Gen == Gen)
			{
				Batcher.OnPartDisabled(E);
			}
		}
	}
	internal static class Batcher
	{
		private sealed class LodPlan
		{
			public int Lod0Active;

			public int Lod0Accepted;

			public readonly List<Renderer> Extra = new List<Renderer>();
		}

		internal static bool Quitting;

		internal static bool Clearing;

		private static readonly Dictionary<WearNTear, Entry> s_entries = new Dictionary<WearNTear, Entry>();

		private static readonly List<Entry> s_entryList = new List<Entry>();

		private static readonly Dictionary<long, Cell> s_cells = new Dictionary<long, Cell>();

		private static readonly HashSet<Cell> s_dirtyCells = new HashSet<Cell>();

		private static readonly HashSet<Group> s_dirtyGroups = new HashSet<Group>();

		private static readonly List<RebuildJob> s_jobs = new List<RebuildJob>();

		private static readonly List<Cell> s_tmpCells = new List<Cell>();

		private static readonly List<Group> s_tmpGroups = new List<Group>();

		private static readonly Stopwatch s_sw = new Stopwatch();

		private static readonly Dictionary<string, bool> s_prefabIneligible = new Dictionary<string, bool>(StringComparer.Ordinal);

		private static HashSet<string> s_shaders;

		private static HashSet<string> s_excludePrefabs;

		private static Transform s_root;

		private static int s_validateCursor;

		internal static int RebuildCount;

		internal static int DiscardedCount;

		internal static double LastRebuildMs;

		internal static double TotalRebuildMs;

		internal static double TotalWorkerMs;

		internal static double UpdateMs;

		private static double s_updateAcc;

		private static int s_updateFrames;

		private static readonly Type[] s_ineligibleTypes = new Type[22]
		{
			typeof(Rigidbody),
			typeof(Animator),
			typeof(SkinnedMeshRenderer),
			typeof(ZSyncTransform),
			typeof(StaticPhysics),
			typeof(Floating),
			typeof(Door),
			typeof(TeleportWorld),
			typeof(ShieldGenerator),
			typeof(ItemStand),
			typeof(ArmorStand),
			typeof(Windmill),
			typeof(MaterialFader),
			typeof(Vagon),
			typeof(Ship),
			typeof(Turret),
			typeof(Catapult),
			typeof(Trap),
			typeof(RandomMaterialValues),
			typeof(MaterialVariation),
			typeof(MaterialVariationWorld),
			typeof(ToggleSwitch)
		};

		private static bool s_forceFragments;

		private static readonly List<GameObject> s_fragmentsHiddenLods = new List<GameObject>();

		private static readonly Dictionary<LODGroup, LodPlan> s_lods = new Dictionary<LODGroup, LodPlan>();

		private static readonly Dictionary<GroupKey, int> s_counts = new Dictionary<GroupKey, int>();

		private static readonly HashSet<Renderer> s_lodOther = new HashSet<Renderer>();

		private static readonly Dictionary<Renderer, LODGroup> s_lod0 = new Dictionary<Renderer, LODGroup>();

		private static readonly Dictionary<LODGroup, Entry> s_lodOwner = new Dictionary<LODGroup, Entry>();

		private static Transform Root
		{
			get
			{
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)s_root == (Object)null)
				{
					s_root = new GameObject("PieceBatcher_Root").transform;
				}
				return s_root;
			}
		}

		internal static void OnBeforeFragments(WearNTear wnt)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			s_forceFragments = false;
			if ((Object)(object)wnt == (Object)null || !s_entries.TryGetValue(wnt, out var value) || !value.Combined)
			{
				return;
			}
			bool flag = false;
			Camera main = Camera.main;
			if ((Object)(object)main != (Object)null)
			{
				Plane[] array = GeometryUtility.CalculateFrustumPlanes(main);
				foreach (Renderer item in value.Hidden)
				{
					if ((Object)(object)item != (Object)null && GeometryUtility.TestPlanesAABB(array, item.bounds))
					{
						flag = true;
						break;
					}
				}
			}
			List<GameObject> list = new List<GameObject>(value.LodExtra);
			Release(value);
			value.Parts = null;
			foreach (GameObject item2 in list)
			{
				if ((Object)(object)item2 != (Object)null && item2.activeSelf)
				{
					item2.SetActive(false);
					s_fragmentsHiddenLods.Add(item2);
				}
			}
			s_forceFragments = flag;
		}

		internal static void OnAfterFragments()
		{
			s_forceFragments = false;
			foreach (GameObject s_fragmentsHiddenLod in s_fragmentsHiddenLods)
			{
				if ((Object)(object)s_fragmentsHiddenLod != (Object)null)
				{
					s_fragmentsHiddenLod.SetActive(true);
				}
			}
			s_fragmentsHiddenLods.Clear();
		}

		internal static bool ConsumeForceFragments()
		{
			bool result = s_forceFragments;
			s_forceFragments = false;
			return result;
		}

		internal static int ContainerState(Container c)
		{
			int num = 0;
			if ((Object)(object)c.m_open != (Object)null && c.m_open.activeSelf)
			{
				num |= 1;
			}
			if ((Object)(object)c.m_closed != (Object)null && c.m_closed.activeSelf)
			{
				num |= 2;
			}
			return num;
		}

		internal static void Register(WearNTear wnt)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)wnt == (Object)null) && !s_entries.ContainsKey(wnt))
			{
				Entry entry = new Entry
				{
					Wnt = wnt,
					Root = ((Component)wnt).gameObject,
					Tr = ((Component)wnt).transform,
					Pos = ((Component)wnt).transform.position
				};
				entry.Prefab = ((Object)entry.Root).name;
				int num = entry.Prefab.IndexOf("(Clone)", StringComparison.Ordinal);
				if (num > 0)
				{
					entry.Prefab = entry.Prefab.Substring(0, num);
				}
				entry.Cell = GetOrCreateCell(entry.Pos);
				entry.Cell.Entries.Add(entry);
				s_entries[wnt] = entry;
				entry.Index = s_entryList.Count;
				s_entryList.Add(entry);
				MarkDirty(entry.Cell);
			}
		}

		internal static void Unregister(WearNTear wnt)
		{
			if (!((Object)(object)wnt == (Object)null) && s_entries.TryGetValue(wnt, out var value))
			{
				Release(value);
				value.Parts = null;
				s_entries.Remove(wnt);
				int num = s_entryList.Count - 1;
				if (value.Index >= 0 && value.Index <= num && s_entryList[value.Index] == value)
				{
					Entry entry = s_entryList[num];
					s_entryList[value.Index] = entry;
					entry.Index = value.Index;
					s_entryList.RemoveAt(num);
				}
				else
				{
					s_entryList.Remove(value);
				}
				value.Index = -1;
				Cell cell = value.Cell;
				value.Wnt = null;
				value.Cell = null;
				cell.Entries.Remove(value);
				if (cell.Entries.Count == 0)
				{
					DestroyCell(cell);
				}
				else
				{
					MarkDirty(cell);
				}
			}
		}

		internal static int VariantState(WearNTear w)
		{
			int num = 0;
			if ((Object)(object)w.m_new != (Object)null && w.m_new.activeSelf)
			{
				num |= 1;
			}
			if ((Object)(object)w.m_worn != (Object)null && w.m_worn.activeSelf)
			{
				num |= 2;
			}
			if ((Object)(object)w.m_broken != (Object)null && w.m_broken.activeSelf)
			{
				num |= 4;
			}
			return num;
		}

		internal static void OnChanged(WearNTear wnt)
		{
			if (!((Object)(object)wnt == (Object)null) && s_entries.TryGetValue(wnt, out var value))
			{
				Release(value);
				value.Parts = null;
				MarkDirty(value.Cell);
			}
		}

		internal static void OnPartDisabled(Entry e)
		{
			Release(e);
			e.Parts = null;
			if (e.Cell != null)
			{
				MarkDirty(e.Cell);
			}
		}

		internal static void SetHighlighted(WearNTear wnt, bool on)
		{
			if (!((Object)(object)wnt == (Object)null) && s_entries.TryGetValue(wnt, out var value) && value.Highlighted != on)
			{
				value.Highlighted = on;
				if (on)
				{
					Release(value);
				}
				MarkDirty(value.Cell);
			}
		}

		internal static void SetMaterialTouched(WearNTear wnt, bool touched)
		{
			if (!((Object)(object)wnt == (Object)null) && s_entries.TryGetValue(wnt, out var value) && value.MaterialTouched != touched)
			{
				value.MaterialTouched = touched;
				if (touched)
				{
					Release(value);
				}
				MarkDirty(value.Cell);
			}
		}

		private static long CellKey(Vector3 p, float size, out int cx, out int cz)
		{
			//IL_0001: 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)
			cx = Mathf.FloorToInt(p.x / size);
			cz = Mathf.FloorToInt(p.z / size);
			return ((long)cx << 32) ^ (uint)cz;
		}

		private static Cell GetOrCreateCell(Vector3 p)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			float value = Plugin.CellSize.Value;
			int cx;
			int cz;
			long key = CellKey(p, value, out cx, out cz);
			if (!s_cells.TryGetValue(key, out var value2))
			{
				value2 = new Cell
				{
					Key = key,
					Origin = new Vector3((float)cx * value, 0f, (float)cz * value)
				};
				s_cells[key] = value2;
			}
			return value2;
		}

		internal static Cell FindCell(Vector3 p)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			int cx;
			int cz;
			long key = CellKey(p, Plugin.CellSize.Value, out cx, out cz);
			s_cells.TryGetValue(key, out var value);
			return value;
		}

		private static void MarkDirty(Cell cell)
		{
			if (cell != null)
			{
				cell.Version++;
				cell.Dirty = true;
				cell.DirtyTime = Time.time;
				s_dirtyCells.Add(cell);
			}
		}

		internal static void MarkAllDirty(bool now = false)
		{
			foreach (Cell value in s_cells.Values)
			{
				MarkDirty(value);
				if (now)
				{
					value.DirtyTime = -1000f;
				}
			}
		}

		private static void DestroyCell(Cell cell)
		{
			foreach (Group group in cell.Groups)
			{
				DestroyGroupObjects(group);
			}
			cell.Groups.Clear();
			cell.Version++;
			s_dirtyCells.Remove(cell);
			s_cells.Remove(cell.Key);
		}

		private static void DestroyGroupObjects(Group g)
		{
			s_dirtyGroups.Remove(g);
			if ((Object)(object)g.Go != (Object)null)
			{
				Object.Destroy((Object)(object)g.Go);
			}
			if ((Object)(object)g.Mesh != (Object)null)
			{
				Object.Destroy((Object)(object)g.Mesh);
			}
			g.Go = null;
			g.Mesh = null;
		}

		internal static void Release(Entry e)
		{
			if (!e.Combined)
			{
				return;
			}
			e.Combined = false;
			e.Gen++;
			foreach (Renderer item in e.Hidden)
			{
				if (!((Object)(object)item == (Object)null))
				{
					item.enabled = true;
					Watcher component = ((Component)item).GetComponent<Watcher>();
					if ((Object)(object)component != (Object)null)
					{
						Object.Destroy((Object)(object)component);
					}
				}
			}
			e.Hidden.Clear();
			e.LodExtra.Clear();
			foreach (Group group in e.Groups)
			{
				group.Ranges.RemoveAll((RangeRef x) => x.E == e);
				if ((Object)(object)group.Go != (Object)null)
				{
					s_dirtyGroups.Add(group);
				}
			}
			e.Groups.Clear();
		}

		private static void ApplyIndices(Group g)
		{
			if ((Object)(object)g.Go == (Object)null || (Object)(object)g.Mesh == (Object)null)
			{
				return;
			}
			if (g.Ranges.Count == 0)
			{
				g.Cell?.Groups.Remove(g);
				DestroyGroupObjects(g);
				return;
			}
			int num = 0;
			foreach (RangeRef range in g.Ranges)
			{
				num += range.Count;
			}
			int[] array = new int[num];
			int num2 = 0;
			foreach (RangeRef range2 in g.Ranges)
			{
				Array.Copy(g.Master, range2.Start, array, num2, range2.Count);
				num2 += range2.Count;
			}
			g.Mesh.SetTriangles(array, 0, false);
		}

		internal static void LateUpdate()
		{
			long timestamp = Stopwatch.GetTimestamp();
			if (s_dirtyGroups.Count > 0)
			{
				s_tmpGroups.Clear();
				s_tmpGroups.AddRange(s_dirtyGroups);
				s_dirtyGroups.Clear();
				foreach (Group s_tmpGroup in s_tmpGroups)
				{
					ApplyIndices(s_tmpGroup);
				}
				s_tmpGroups.Clear();
			}
			AccountTime(timestamp);
		}

		private static void AccountTime(long t0)
		{
			s_updateAcc += (double)(Stopwatch.GetTimestamp() - t0) * 1000.0 / (double)Stopwatch.Frequency;
		}

		internal static void Update()
		{
			if (Quitting)
			{
				return;
			}
			long timestamp = Stopwatch.GetTimestamp();
			s_updateFrames++;
			if (s_updateFrames >= 60)
			{
				UpdateMs = s_updateAcc / (double)s_updateFrames;
				s_updateAcc = 0.0;
				s_updateFrames = 0;
			}
			double num = Plugin.FrameBudgetMs.Value;
			s_sw.Restart();
			for (int i = 0; i < s_jobs.Count; i++)
			{
				RebuildJob rebuildJob = s_jobs[i];
				if (!rebuildJob.Done)
				{
					continue;
				}
				s_jobs.RemoveAt(i);
				i--;
				if (rebuildJob.Cell.Job == rebuildJob)
				{
					rebuildJob.Cell.Job = null;
				}
				if (rebuildJob.Error != null)
				{
					Plugin.Log.LogWarning((object)$"фоновая сборка ячейки {rebuildJob.Cell.Origin.x:0},{rebuildJob.Cell.Origin.z:0} не удалась: {rebuildJob.Error.GetType().Name}: {rebuildJob.Error.Message}");
				}
				else
				{
					if (rebuildJob.Version == rebuildJob.Cell.Version && s_cells.ContainsKey(rebuildJob.Cell.Key))
					{
						ApplyJob(rebuildJob);
						break;
					}
					DiscardedCount++;
				}
			}
			if (s_dirtyCells.Count > 0 && Plugin.Enabled.Value)
			{
				float time = Time.time;
				float value = Plugin.RebuildDelay.Value;
				s_tmpCells.Clear();
				foreach (Cell s_dirtyCell in s_dirtyCells)
				{
					if (s_dirtyCell.Job == null && time - s_dirtyCell.DirtyTime >= value)
					{
						s_tmpCells.Add(s_dirtyCell);
					}
				}
				foreach (Cell s_tmpCell in s_tmpCells)
				{
					if (s_sw.Elapsed.TotalMilliseconds > num)
					{
						break;
					}
					s_dirtyCells.Remove(s_tmpCell);
					s_tmpCell.Dirty = false;
					StartJob(s_tmpCell);
				}
				s_tmpCells.Clear();
			}
			ValidateSlice();
			AccountTime(timestamp);
		}

		private static void ValidateSlice()
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			int count = s_entryList.Count;
			if (count == 0)
			{
				return;
			}
			int num = count / 60 + 1;
			for (int i = 0; i < num; i++)
			{
				if (s_validateCursor >= s_entryList.Count)
				{
					s_validateCursor = 0;
				}
				Entry entry = s_entryList[s_validateCursor++];
				if (!entry.Combined)
				{
					continue;
				}
				if ((Object)(object)entry.Tr == (Object)null)
				{
					Release(entry);
					entry.Parts = null;
					continue;
				}
				Vector3 val = entry.Tr.position - entry.Pos;
				if (((Vector3)(ref val)).sqrMagnitude > 0.0001f)
				{
					entry.Moved = true;
					Release(entry);
					entry.Parts = null;
					MarkDirty(entry.Cell);
				}
			}
		}

		private static void EnsureFilters()
		{
			if (s_shaders == null)
			{
				s_shaders = Split(Plugin.Shaders.Value);
				s_excludePrefabs = Split(Plugin.ExcludePrefabs.Value);
			}
		}

		private static HashSet<string> Split(string s)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			string[] array = (s ?? "").Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length > 0)
				{
					hashSet.Add(text);
				}
			}
			return hashSet;
		}

		private static bool IsIneligible(Entry e)
		{
			if (e.Checked)
			{
				return e.Ineligible;
			}
			e.Checked = true;
			EnsureFilters();
			if ((Object)(object)e.Tr != (Object)null && (Object)(object)e.Tr.parent != (Object)null)
			{
				e.Ineligible = true;
				return true;
			}
			if (!s_prefabIneligible.TryGetValue(e.Prefab, out var value))
			{
				value = s_excludePrefabs.Contains(e.Prefab);
				if (!value)
				{
					Type[] array = s_ineligibleTypes;
					foreach (Type type in array)
					{
						if ((Object)(object)e.Root.GetComponentInChildren(type, true) != (Object)null)
						{
							value = true;
							break;
						}
					}
				}
				s_prefabIneligible[e.Prefab] = value;
			}
			e.Ineligible = value;
			return value;
		}

		private static void StartJob(Cell cell)
		{
			long timestamp = Stopwatch.GetTimestamp();
			if (!Plugin.Enabled.Value)
			{
				return;
			}
			EnsureFilters();
			RebuildJob rebuildJob = new RebuildJob
			{
				Cell = cell,
				Version = cell.Version,
				MaxVertices = Plugin.MaxVertices.Value
			};
			s_lods.Clear();
			s_counts.Clear();
			s_lodOwner.Clear();
			foreach (Entry entry in cell.Entries)
			{
				if ((Object)(object)entry.Wnt == (Object)null || (Object)(object)entry.Root == (Object)null || entry.Excluded || IsIneligible(entry))
				{
					continue;
				}
				if (entry.Parts != null && !PartsStillValid(entry))
				{
					entry.Parts = null;
				}
				if (entry.Parts == null)
				{
					if (entry.Combined)
					{
						Release(entry);
					}
					entry.Parts = CollectParts(entry, cell);
				}
				else
				{
					foreach (PartPlan part in entry.Parts)
					{
						if ((Object)(object)part.Lod != (Object)null && !s_lods.ContainsKey(part.Lod))
						{
							s_lods[part.Lod] = BuildLodPlan(part.Lod, entry);
						}
					}
				}
				foreach (PartPlan part2 in entry.Parts)
				{
					if (!((Object)(object)part2.R == (Object)null) && (!((Object)(object)part2.Lod != (Object)null) || s_lods.ContainsKey(part2.Lod)))
					{
						part2.Accepted = false;
						rebuildJob.Parts.Add(part2);
					}
				}
			}
			int value = Plugin.MinParts.Value;
			foreach (PartPlan part3 in rebuildJob.Parts)
			{
				GroupKey[] keys = part3.Keys;
				foreach (GroupKey key in keys)
				{
					s_counts.TryGetValue(key, out var value2);
					s_counts[key] = value2 + 1;
				}
			}
			foreach (PartPlan part4 in rebuildJob.Parts)
			{
				part4.Accepted = true;
				GroupKey[] keys = part4.Keys;
				foreach (GroupKey key2 in keys)
				{
					if (s_counts[key2] < value)
					{
						part4.Accepted = false;
						break;
					}
				}
				if (part4.Accepted && (Object)(object)part4.Lod != (Object)null)
				{
					s_lods[part4.Lod].Lod0Accepted++;
				}
			}
			foreach (PartPlan part5 in rebuildJob.Parts)
			{
				if (part5.Accepted && (Object)(object)part5.Lod != (Object)null)
				{
					LodPlan lodPlan = s_lods[part5.Lod];
					if (lodPlan.Lod0Accepted != lodPlan.Lod0Active)
					{
						part5.Accepted = false;
					}
				}
			}
			rebuildJob.Parts.RemoveAll((PartPlan p) => !p.Accepted);
			foreach (PartPlan part6 in rebuildJob.Parts)
			{
				if (!rebuildJob.ToHide.TryGetValue(part6.E, out var value3))
				{
					value3 = new HashSet<Renderer>();
					rebuildJob.ToHide[part6.E] = value3;
				}
				value3.Add((Renderer)(object)part6.R);
				if (!((Object)(object)part6.Lod != (Object)null))
				{
					continue;
				}
				if (!rebuildJob.ToHideExtra.TryGetValue(part6.E, out var value4))
				{
					value4 = new HashSet<Renderer>();
					rebuildJob.ToHideExtra[part6.E] = value4;
				}
				foreach (Renderer item in s_lods[part6.Lod].Extra)
				{
					value3.Add(item);
					value4.Add(item);
				}
			}
			s_lods.Clear();
			s_counts.Clear();
			s_lodOwner.Clear();
			rebuildJob.CollectMs = (double)(Stopwatch.GetTimestamp() - timestamp) * 1000.0 / (double)Stopwatch.Frequency;
			cell.Job = rebuildJob;
			s_jobs.Add(rebuildJob);
			if (rebuildJob.Parts.Count == 0)
			{
				rebuildJob.Done = true;
			}
			else
			{
				ThreadPool.QueueUserWorkItem(RunJob, rebuildJob);
			}
		}

		private static bool PartsStillValid(Entry e)
		{
			foreach (PartPlan part in e.Parts)
			{
				if ((Object)(object)part.R == (Object)null || !((Component)part.R).gameObject.activeInHierarchy)
				{
					return false;
				}
				if (!((Renderer)part.R).enabled && (!e.Combined || !e.Hidden.Contains((Renderer)(object)part.R)))
				{
					return false;
				}
			}
			return true;
		}

		private static bool SameRenderers(List<Renderer> hidden, HashSet<Renderer> wanted)
		{
			if (hidden.Count != wanted.Count)
			{
				return false;
			}
			foreach (Renderer item in hidden)
			{
				if (!wanted.Contains(item))
				{
					return false;
				}
			}
			return true;
		}

		private static LodPlan BuildLodPlan(LODGroup lg, Entry e)
		{
			LodPlan lodPlan = new LodPlan();
			LOD[] lODs = lg.GetLODs();
			if (lODs == null || lODs.Length == 0)
			{
				return lodPlan;
			}
			Renderer[] renderers = lODs[0].renderers;
			foreach (Renderer val in renderers)
			{
				if ((Object)(object)val != (Object)null && ((Component)val).gameObject.activeInHierarchy && (val.enabled || (e.Combined && e.Hidden.Contains(val))))
				{
					lodPlan.Lod0Active++;
				}
			}
			for (int j = 1; j < lODs.Length; j++)
			{
				renderers = lODs[j].renderers;
				foreach (Renderer val2 in renderers)
				{
					if ((Object)(object)val2 != (Object)null && ((Component)val2).gameObject.activeInHierarchy && (val2.enabled || (e.Combined && e.Hidden.Contains(val2))))
					{
						lodPlan.Extra.Add(val2);
					}
				}
			}
			return lodPlan;
		}

		private static List<PartPlan> CollectParts(Entry e, Cell cell)
		{
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: Unknown result type (might be due to invalid IL or missing references)
			//IL_0250: Unknown result type (might be due to invalid IL or missing references)
			//IL_0255: Unknown result type (might be due to invalid IL or missing references)
			//IL_025a: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02db: Unknown result type (might be due to invalid IL or missing references)
			List<PartPlan> list = new List<PartPlan>();
			GameObject root = e.Root;
			s_lod0.Clear();
			s_lodOther.Clear();
			LODGroup[] componentsInChildren = root.GetComponentsInChildren<LODGroup>(false);
			foreach (LODGroup val in componentsInChildren)
			{
				LOD[] lODs = val.GetLODs();
				if (lODs == null || lODs.Length == 0)
				{
					continue;
				}
				s_lods[val] = BuildLodPlan(val, e);
				Renderer[] renderers = lODs[0].renderers;
				foreach (Renderer val2 in renderers)
				{
					if ((Object)(object)val2 != (Object)null)
					{
						s_lod0[val2] = val;
					}
				}
				for (int k = 1; k < lODs.Length; k++)
				{
					renderers = lODs[k].renderers;
					foreach (Renderer val3 in renderers)
					{
						if ((Object)(object)val3 != (Object)null && !s_lod0.ContainsKey(val3))
						{
							s_lodOther.Add(val3);
						}
					}
				}
			}
			Matrix4x4 val4 = Matrix4x4.Translate(-cell.Origin);
			MeshRenderer[] componentsInChildren2 = root.GetComponentsInChildren<MeshRenderer>(false);
			foreach (MeshRenderer val5 in componentsInChildren2)
			{
				if (!((Renderer)val5).enabled || s_lodOther.Contains((Renderer)(object)val5) || (Object)(object)((Component)val5).GetComponent<RenderGroupSubscriber>() != (Object)null)
				{
					continue;
				}
				MeshFilter component = ((Component)val5).GetComponent<MeshFilter>();
				if ((Object)(object)component == (Object)null || (Object)(object)component.sharedMesh == (Object)null)
				{
					continue;
				}
				Mesh sharedMesh = component.sharedMesh;
				Material[] sharedMaterials = ((Renderer)val5).sharedMaterials;
				if (sharedMaterials == null || sharedMaterials.Length == 0 || sharedMaterials.Length != sharedMesh.subMeshCount)
				{
					continue;
				}
				bool flag = true;
				Material[] array = sharedMaterials;
				foreach (Material val6 in array)
				{
					if ((Object)(object)val6 == (Object)null || (Object)(object)val6.shader == (Object)null || !s_shaders.Contains(((Object)val6.shader).name) || val6.renderQueue > 2500)
					{
						flag = false;
						break;
					}
				}
				if (!flag)
				{
					continue;
				}
				MeshData meshData = MeshCache.Get(sharedMesh);
				if (meshData.Ok)
				{
					Matrix4x4 m = val4 * ((Component)val5).transform.localToWorldMatrix;
					GroupKey[] array2 = new GroupKey[sharedMaterials.Length];
					for (int l = 0; l < sharedMaterials.Length; l++)
					{
						array2[l] = GroupKey.Of(val5, sharedMaterials[l], meshData.Signature);
					}
					s_lod0.TryGetValue((Renderer)(object)val5, out var value);
					PartPlan obj = new PartPlan
					{
						E = e,
						R = val5,
						D = meshData,
						M = m
					};
					Matrix4x4 inverse = ((Matrix4x4)(ref m)).inverse;
					obj.Nm = ((Matrix4x4)(ref inverse)).transpose;
					obj.Flip = ((Matrix4x4)(ref m)).determinant < 0f;
					obj.Keys = array2;
					obj.Lod = value;
					list.Add(obj);
				}
			}
			s_lod0.Clear();
			s_lodOther.Clear();
			return list;
		}

		private static void RunJob(object state)
		{
			RebuildJob rebuildJob = (RebuildJob)state;
			long timestamp = Stopwatch.GetTimestamp();
			try
			{
				Dictionary<GroupKey, List<GroupBuilder>> dictionary = new Dictionary<GroupKey, List<GroupBuilder>>();
				foreach (PartPlan part in rebuildJob.Parts)
				{
					for (int i = 0; i < part.Keys.Length; i++)
					{
						GroupKey key = part.Keys[i];
						if (!dictionary.TryGetValue(key, out var value))
						{
							value = (dictionary[key] = new List<GroupBuilder>());
						}
						GroupBuilder groupBuilder = ((value.Count > 0) ? value[value.Count - 1] : null);
						if (groupBuilder == null || (groupBuilder.Pos.Count > 0 && groupBuilder.Pos.Count + part.D.Pos.Length > rebuildJob.MaxVertices))
						{
							groupBuilder = new GroupBuilder
							{
								Key = key
							};
							value.Add(groupBuilder);
						}
						groupBuilder.Add(part, i);
					}
				}
				foreach (List<GroupBuilder> value2 in dictionary.Values)
				{
					foreach (GroupBuilder item in value2)
					{
						if (item.Pos.Count > 0)
						{
							item.Master = item.Idx.ToArray();
							rebuildJob.Builders.Add(item);
						}
					}
				}
			}
			catch (Exception error)
			{
				rebuildJob.Error = error;
			}
			rebuildJob.WorkerMs = (double)(Stopwatch.GetTimestamp() - timestamp) * 1000.0 / (double)Stopwatch.Frequency;
			Thread.MemoryBarrier();
			rebuildJob.Done = true;
		}

		private static void ApplyJob(RebuildJob job)
		{
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			long timestamp = Stopwatch.GetTimestamp();
			Cell cell = job.Cell;
			foreach (Entry entry in cell.Entries)
			{
				if (entry.Combined && !job.ToHide.ContainsKey(entry))
				{
					Release(entry);
				}
			}
			foreach (Entry entry2 in cell.Entries)
			{
				entry2.Groups.Clear();
			}
			List<Group> list = new List<Group>(job.Builders.Count);
			int num = 0;
			foreach (GroupBuilder builder in job.Builders)
			{
				Group obj = BuildGroup(builder, cell, list.Count);
				list.Add(obj);
				num += obj.VertexCount;
			}
			int num2 = 0;
			foreach (KeyValuePair<Entry, HashSet<Renderer>> item in job.ToHide)
			{
				Entry key = item.Key;
				if ((Object)(object)key.Wnt == (Object)null || key.Cell != cell)
				{
					continue;
				}
				job.ToHideExtra.TryGetValue(key, out var value);
				if (key.Combined && !SameRenderers(key.Hidden, item.Value))
				{
					Release(key);
				}
				if (!key.Combined)
				{
					key.Combined = true;
					key.Pos = key.Tr.position;
					foreach (Renderer item2 in item.Value)
					{
						if (!((Object)(object)item2 == (Object)null))
						{
							item2.enabled = false;
							key.Hidden.Add(item2);
							if (value != null && value.Contains(item2))
							{
								key.LodExtra.Add(((Component)item2).gameObject);
							}
							Watcher watcher = ((Component)item2).gameObject.AddComponent<Watcher>();
							watcher.E = key;
							watcher.Gen = key.Gen;
						}
					}
				}
				num2++;
			}
			foreach (Group group in cell.Groups)
			{
				DestroyGroupObjects(group);
			}
			cell.Groups.Clear();
			cell.Groups.AddRange(list);
			RebuildCount++;
			LastRebuildMs = job.CollectMs + (double)(Stopwatch.GetTimestamp() - timestamp) * 1000.0 / (double)Stopwatch.Frequency;
			TotalRebuildMs += LastRebuildMs;
			TotalWorkerMs += job.WorkerMs;
			if (Plugin.Verbose.Value)
			{
				Plugin.Log.LogInfo((object)$"ячейка {cell.Origin.x:0},{cell.Origin.z:0}: деталей {cell.Entries.Count}, склеено {num2}, мешей {list.Count}, вершин {num}, главный поток {LastRebuildMs:0.0} мс, фон {job.WorkerMs:0.0} мс");
			}
		}

		private static Group BuildGroup(GroupBuilder b, Cell cell, int index)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_020f: Expected O, but got Unknown
			//IL_0238: Unknown result type (might be due to invalid IL or missing references)
			//IL_026c: Unknown result type (might be due to invalid IL or missing references)
			//IL_028e: Unknown result type (might be due to invalid IL or missing references)
			//IL_029f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			Mesh val = new Mesh
			{
				name = "PieceBatcher"
			};
			val.indexFormat = (IndexFormat)(b.Pos.Count > 65535);
			val.SetVertices(b.Pos);
			int count = b.Pos.Count;
			if (b.Nrm.Count == count)
			{
				val.SetNormals(b.Nrm);
			}
			if (b.Tan.Count == count)
			{
				val.SetTangents(b.Tan);
			}
			if (b.Col.Count == count)
			{
				val.SetColors(b.Col);
			}
			for (int i = 0; i < 4; i++)
			{
				if (b.Uv[i].Count != count)
				{
					continue;
				}
				switch (b.First.UvDim[i])
				{
				case 2:
				{
					List<Vector2> list2 = new List<Vector2>(count);
					foreach (Vector4 item in b.Uv[i])
					{
						list2.Add(new Vector2(item.x, item.y));
					}
					val.SetUVs(i, list2);
					break;
				}
				case 3:
				{
					List<Vector3> list = new List<Vector3>(count);
					foreach (Vector4 item2 in b.Uv[i])
					{
						list.Add(new Vector3(item2.x, item2.y, item2.z));
					}
					val.SetUVs(i, list);
					break;
				}
				default:
					val.SetUVs(i, b.Uv[i]);
					break;
				}
			}
			int[] array = b.Master ?? b.Idx.ToArray();
			val.SetTriangles(array, 0, true);
			val.UploadMeshData(false);
			GameObject val2 = new GameObject($"PB_{cell.Origin.x:0}_{cell.Origin.z:0}_{index}");
			val2.layer = b.Key.Layer;
			val2.transform.SetParent(Root, false);
			val2.transform.position = cell.Origin;
			val2.AddComponent<MeshFilter>().sharedMesh = val;
			MeshRenderer obj = val2.AddComponent<MeshRenderer>();
			((Renderer)obj).sharedMaterial = b.Key.Mat;
			((Renderer)obj).shadowCastingMode = b.Key.Cast;
			((Renderer)obj).receiveShadows = b.Key.Receive;
			((Renderer)obj).lightProbeUsage = b.Key.Lp;
			((Renderer)obj).reflectionProbeUsage = b.Key.Rp;
			((Renderer)obj).motionVectorGenerationMode = b.Key.Mv;
			((Renderer)obj).renderingLayerMask = b.Key.RenderingLayerMask;
			((Renderer)obj).allowOcclusionWhenDynamic = b.Key.Occl;
			Group obj2 = new Group
			{
				Key = b.Key,
				Cell = cell,
				Go = val2,
				Mesh = val,
				Master = array,
				VertexCount = count
			};
			obj2.Ranges.AddRange(b.Ranges);
			foreach (RangeRef range in b.Ranges)
			{
				if (!range.E.Groups.Contains(obj2))
				{
					range.E.Groups.Add(obj2);
				}
			}
			return obj2;
		}

		internal static void OnEnabledChanged()
		{
			if (Plugin.Enabled.Value)
			{
				MarkAllDirty(now: true);
			}
			else
			{
				ReleaseAll();
			}
		}

		private static void ReleaseAll()
		{
			foreach (Entry s_entry in s_entryList)
			{
				Release(s_entry);
			}
			foreach (Cell value in s_cells.Values)
			{
				foreach (Group group in value.Groups)
				{
					DestroyGroupObjects(group);
				}
				value.Groups.Clear();
				value.Dirty = false;
				value.Version++;
				value.Job = null;
			}
			s_jobs.Clear();
			s_dirtyCells.Clear();
			s_dirtyGroups.Clear();
		}

		internal static void Reset()
		{
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			ReleaseAll();
			s_shaders = null;
			s_excludePrefabs = null;
			s_prefabIneligible.Clear();
			foreach (Cell value in s_cells.Values)
			{
				value.Entries.Clear();
			}
			s_cells.Clear();
			foreach (Entry s_entry in s_entryList)
			{
				s_entry.Checked = false;
				s_entry.Ineligible = false;
				s_entry.Moved = false;
				s_entry.Parts = null;
				if ((Object)(object)s_entry.Tr != (Object)null)
				{
					s_entry.Pos = s_entry.Tr.position;
				}
				s_entry.Cell = GetOrCreateCell(s_entry.Pos);
				s_entry.Cell.Entries.Add(s_entry);
				MarkDirty(s_entry.Cell);
			}
		}

		internal static void Clear()
		{
			Clearing = true;
			try
			{
				ReleaseAll();
			}
			finally
			{
				s_entries.Clear();
				s_entryList.Clear();
				s_cells.Clear();
				s_dirtyCells.Clear();
				s_dirtyGroups.Clear();
				s_jobs.Clear();
				MeshCache.Clear();
				s_validateCursor = 0;
				Clearing = false;
			}
		}

		internal static string Stats()
		{
			int num = 0;
			int num2 = 0;
			foreach (Entry s_entry in s_entryList)
			{
				if (s_entry.Combined)
				{
					num++;
					num2 += s_entry.Hidden.Count;
				}
			}
			int num3 = 0;
			int num4 = 0;
			foreach (Cell value in s_cells.Values)
			{
				num3 += value.Groups.Count;
				foreach (Group group in value.Groups)
				{
					num4 += group.VertexCount;
				}
			}
			string text = (Plugin.Enabled.Value ? "вкл" : "выкл");
			return $"PieceBatcher {text}: деталей {s_entryList.Count}, склеено {num} (рендереров выключено {num2}), ячеек {s_cells.Count}, мешей {num3}, вершин {num4}, ждут пересборки {s_dirtyCells.Count}, в фоне {s_jobs.Count}; пересборок {RebuildCount} (выброшено {DiscardedCount}), последняя {LastRebuildMs:0.0} мс в главном потоке, всего главный {TotalRebuildMs:0} мс / фон {TotalWorkerMs:0} мс; свои Update+LateUpdate {UpdateMs:0.00} мс/кадр; мешей в кэше {MeshCache.Count}";
		}

		internal static string CellStats(Vector3 p)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			Cell cell = FindCell(p);
			if (cell == null)
			{
				return "в этой ячейке деталей нет";
			}
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			foreach (Entry entry in cell.Entries)
			{
				if (entry.Combined)
				{
					num++;
				}
				if (entry.Highlighted || entry.MaterialTouched || entry.Moved)
				{
					num2++;
				}
				if (entry.Ineligible)
				{
					num3++;
				}
			}
			int num4 = 0;
			foreach (Group group in cell.Groups)
			{
				num4 += group.VertexCount;
			}
			string text = ((cell.Job != null) ? "собирается в фоне" : (cell.Dirty ? "ждёт пересборки" : "собрана"));
			return $"ячейка {cell.Origin.x:0},{cell.Origin.z:0} ({Plugin.CellSize.Value:0} м): деталей {cell.Entries.Count}, склеено {num}, временно сами {num2}, не подлежат {num3}, мешей {cell.Groups.Count}, вершин {num4}, {text}";
		}
	}
	internal static class Diag
	{
		private const int N = 600;

		private static readonly float[] s_frames = new float[600];

		private static int s_idx;

		private static int s_count;

		private static readonly FrameTiming[] s_timings = (FrameTiming[])(object)new FrameTiming[1];

		private static bool s_timingFailed;

		private static int s_gcLast;

		private static float s_gcLastTime;

		public static void Tick()
		{
			s_frames[s_idx] = Time.unscaledDeltaTime;
			s_idx = (s_idx + 1) % 600;
			if (s_count < 600)
			{
				s_count++;
			}
			if (!s_timingFailed)
			{
				try
				{
					FrameTimingManager.CaptureFrameTimings();
				}
				catch (Exception)
				{
					s_timingFailed = true;
				}
			}
		}

		public static string FrameLine()
		{
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			if (s_count < 10)
			{
				return "кадры: мало данных";
			}
			float[] array = new float[s_count];
			Array.Copy(s_frames, array, s_count);
			float num = 0f;
			float[] array2 = array;
			foreach (float num2 in array2)
			{
				num += num2;
			}
			Array.Sort(array);
			float num3 = num / (float)s_count;
			float num4 = array[Mathf.Clamp((int)(0.99f * (float)(s_count - 1)), 0, s_count - 1)];
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append($"кадры за {num:0.0} с: среднее {1f / num3:0} FPS ({num3 * 1000f:0.0} мс), 1% худших {1f / num4:0} FPS ({num4 * 1000f:0.0} мс)");
			int num5 = GC.CollectionCount(0);
			if (s_gcLastTime > 0f)
			{
				stringBuilder.Append($"; сборок мусора {num5 - s_gcLast} за {Time.unscaledTime - s_gcLastTime:0} с");
			}
			s_gcLast = num5;
			s_gcLastTime = Time.unscaledTime;
			if (!s_timingFailed)
			{
				try
				{
					if (FrameTimingManager.GetLatestTimings(1u, s_timings) != 0)
					{
						FrameTiming val = s_timings[0];
						stringBuilder.Append($"; движок: CPU {val.cpuFrameTime:0.0} мс (главный поток {val.cpuMainThreadFrameTime:0.0}, поток рендера {val.cpuRenderThreadFrameTime:0.0}), GPU {val.gpuFrameTime:0.0} мс");
					}
				}
				catch (Exception)
				{
					s_timingFailed = true;
				}
			}
			return stringBuilder.ToString();
		}

		public static string SceneLine()
		{
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			int num2 = 0;
			int num3 = LayerMask.NameToLayer("piece");
			int num4 = 0;
			MeshRenderer[] array = Object.FindObjectsByType<MeshRenderer>((FindObjectsSortMode)0);
			foreach (MeshRenderer val in array)
			{
				if (((Renderer)val).enabled && ((Component)val).gameObject.activeInHierarchy)
				{
					num++;
					if (((Renderer)val).isVisible)
					{
						num2++;
					}
					if (((Component)val).gameObject.layer == num3)
					{
						num4++;
					}
				}
			}
			int num5 = 0;
			SkinnedMeshRenderer[] array2 = Object.FindObjectsByType<SkinnedMeshRenderer>((FindObjectsSortMode)0);
			foreach (SkinnedMeshRenderer val2 in array2)
			{
				if (((Renderer)val2).enabled && ((Component)val2).gameObject.activeInHierarchy)
				{
					num5++;
				}
			}
			int num6 = 0;
			int num7 = 0;
			Light[] array3 = Object.FindObjectsByType<Light>((FindObjectsSortMode)0);
			foreach (Light val3 in array3)
			{
				if (((Behaviour)val3).enabled && ((Component)val3).gameObject.activeInHierarchy && !(val3.intensity <= 0f))
				{
					num6++;
					if ((int)val3.shadows != 0)
					{
						num7++;
					}
				}
			}
			int num8 = 0;
			ParticleSystem[] array4 = Object.FindObjectsByType<ParticleSystem>((FindObjectsSortMode)0);
			foreach (ParticleSystem val4 in array4)
			{
				if (val4.isPlaying && val4.particleCount > 0)
				{
					num8++;
				}
			}
			int count = Character.GetAllCharacters().Count;
			int totalSmoke = Smoke.GetTotalSmoke();
			int count2 = WearNTear.GetAllInstances().Count;
			int num9 = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.NrOfInstances() : 0);
			int num10 = 0;
			int num11 = 0;
			try
			{
				HashSet<LightLod> hashSet = AccessTools.Field(typeof(LightLod), "m_lights")?.GetValue(null) as HashSet<LightLod>;
				FieldInfo fieldInfo = AccessTools.Field(typeof(LightLod), "m_light");
				if (hashSet != null && fieldInfo != null)
				{
					foreach (LightLod item in hashSet)
					{
						if ((Object)(object)item == (Object)null)
						{
							continue;
						}
						object? value = fieldInfo.GetValue(item);
						Light val5 = (Light)((value is Light) ? value : null);
						if (!((Object)(object)val5 == (Object)null) && ((Behaviour)val5).enabled && ((Component)val5).gameObject.activeInHierarchy)
						{
							num10++;
							if ((int)val5.shadows != 0)
							{
								num11++;
							}
						}
					}
				}
			}
			catch (Exception)
			{
				num10 = -1;
			}
			return $"сцена: рендереров {num} (в кадре {num2}, на слое piece {num4}), скелетных {num5}, источников света {num6} (с тенями {num7}; точечных игры {num10}, из них с тенями {num11}), частиц {num8}, существ {count}, дыма {totalSmoke}, деталей WearNTear {count2}, сетевых объектов {num9}";
		}
	}
	internal static class LoopProbes
	{
		private sealed class Probe
		{
		}

		private static string[] s_names = new string[0];

		private static long[] s_begin = new long[0];

		private static double[] s_acc = new double[0];

		private static long s_frameStart;

		private static double s_frameAcc;

		private static int s_frames;

		private static bool s_installed;

		private static readonly Dictionary<string, string> s_hints = new Dictionary<string, string>(StringComparer.Ordinal)
		{
			{ "Initialization.PlayerUpdateTime", "ожидание начала кадра: vsync, лимит FPS или GPU" },
			{ "EarlyUpdate.UpdateInputManager", "ввод" },
			{ "EarlyUpdate.ExecuteMainThreadJobs", "задачи главного потока" },
			{ "FixedUpdate.PhysicsFixedUpdate", "физика PhysX" },
			{ "FixedUpdate.ScriptRunBehaviourFixedUpdate", "скрипты FixedUpdate: существа, синхронизация, дым" },
			{ "FixedUpdate.ScriptRunDelayedFixedFrameRate", "корутины FixedUpdate" },
			{ "PreUpdate.PhysicsUpdate", "физика: синхронизация трансформов" },
			{ "PreUpdate.AIUpdate", "навигация NavMesh" },
			{ "Update.ScriptRunBehaviourUpdate", "скрипты Update" },
			{ "Update.ScriptRunDelayedDynamicFrameRate", "корутины" },
			{ "PreLateUpdate.DirectorUpdateAnimationBegin", "анимация: старт" },
			{ "PreLateUpdate.DirectorUpdateAnimationEnd", "анимация: применение" },
			{ "PreLateUpdate.ParticleSystemBeginUpdateAll", "частицы: старт" },
			{ "PreLateUpdate.ScriptRunBehaviourLateUpdate", "скрипты LateUpdate" },
			{ "PostLateUpdate.ParticleSystemEndUpdateAll", "частицы: завершение" },
			{ "PostLateUpdate.UpdateAllRenderers", "рендереры: подготовка" },
			{ "PostLateUpdate.UpdateAllSkinnedMeshes", "скелетные меши" },
			{ "PostLateUpdate.UpdateAudio", "звук" },
			{ "PostLateUpdate.PlayerUpdateCanvases", "интерфейс" },
			{ "PostLateUpdate.PlayerEmitCanvasGeometry", "интерфейс: геометрия" },
			{ "PostLateUpdate.FinishFrameRendering", "рендер: отсечение и подача (главный поток)" },
			{ "PostLateUpdate.PresentAfterDraw", "показ кадра: ожидание GPU и потока рендера" },
			{ "PostLateUpdate.UpdateCustomRenderTextures", "пользовательские текстуры" },
			{ "PostLateUpdate.PhysicsSkinnedClothBeginUpdate", "ткань: старт" },
			{ "PostLateUpdate.PhysicsSkinnedClothFinishUpdate", "ткань: завершение" }
		};

		public static bool Installed => s_installed;

		public static void Install()
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0196: 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_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			if (s_installed)
			{
				return;
			}
			PlayerLoopSystem currentPlayerLoop = PlayerLoop.GetCurrentPlayerLoop();
			if (currentPlayerLoop.subSystemList == null)
			{
				return;
			}
			List<string> list = new List<string>();
			for (int i = 0; i < currentPlayerLoop.subSystemList.Length; i++)
			{
				PlayerLoopSystem val = currentPlayerLoop.subSystemList[i];
				if (val.subSystemList == null || val.subSystemList.Length == 0)
				{
					continue;
				}
				List<PlayerLoopSystem> list2 = new List<PlayerLoopSystem>(val.subSystemList.Length * 3);
				PlayerLoopSystem[] subSystemList = val.subSystemList;
				foreach (PlayerLoopSystem val2 in subSystemList)
				{
					if (!(val2.type == typeof(Probe)))
					{
						int count = list.Count;
						list.Add(((val.type != null) ? val.type.Name : "?") + "." + ((val2.type != null) ? val2.type.Name : "?"));
						list2.Add(Marker(count, begin: true));
						list2.Add(val2);
						list2.Add(Marker(count, begin: false));
					}
				}
				val.subSystemList = list2.ToArray();
				currentPlayerLoop.subSystemList[i] = val;
			}
			s_names = list.ToArray();
			s_begin = new long[s_names.Length];
			s_acc = new double[s_names.Length];
			s_frameStart = 0L;
			s_frameAcc = 0.0;
			s_frames = 0;
			PlayerLoop.SetPlayerLoop(currentPlayerLoop);
			s_installed = true;
		}

		public static void Uninstall()
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			if (!s_installed)
			{
				return;
			}
			PlayerLoopSystem currentPlayerLoop = PlayerLoop.GetCurrentPlayerLoop();
			if (currentPlayerLoop.subSystemList != null)
			{
				for (int i = 0; i < currentPlayerLoop.subSystemList.Length; i++)
				{
					PlayerLoopSystem val = currentPlayerLoop.subSystemList[i];
					if (val.subSystemList == null)
					{
						continue;
					}
					List<PlayerLoopSystem> list = new List<PlayerLoopSystem>(val.subSystemList.Length);
					PlayerLoopSystem[] subSystemList = val.subSystemList;
					foreach (PlayerLoopSystem val2 in subSystemList)
					{
						if (val2.type != typeof(Probe))
						{
							list.Add(val2);
						}
					}
					val.subSystemList = list.ToArray();
					currentPlayerLoop.subSystemList[i] = val;
				}
				PlayerLoop.SetPlayerLoop(currentPlayerLoop);
			}
			s_installed = false;
		}

		private static PlayerLoopSystem Marker(int index, bool begin)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			return new PlayerLoopSystem
			{
				type = typeof(Probe),
				updateDelegate = (begin ? ((UpdateFunction)delegate
				{
					Begin(index);
				}) : ((UpdateFunction)delegate
				{
					End(index);
				}))
			};
		}

		private static void Begin(int i)
		{
			long timestamp = Stopwatch.GetTimestamp();
			if (i == 0)
			{
				if (s_frameStart != 0L)
				{
					s_frameAcc += (double)(timestamp - s_frameStart) * 1000.0 / (double)Stopwatch.Frequency;
					s_frames++;
				}
				s_frameStart = timestamp;
			}
			s_begin[i] = timestamp;
		}

		private static void End(int i)
		{
			s_acc[i] += (double)(Stopwatch.GetTimestamp() - s_begin[i]) * 1000.0 / (double)Stopwatch.Frequency;
		}

		public static void ResetWindow()
		{
			Array.Clear(s_acc, 0, s_acc.Length);
			s_frameAcc = 0.0;
			s_frames = 0;
		}

		public static string Report(int top = 14)
		{
			if (!s_installed)
			{
				return "пробы цикла кадра выключены (LoopProbes в конфиге)";
			}
			if (s_frames < 10)
			{
				return "пробы цикла кадра: мало данных, подожди секунду и повтори";
			}
			int[] array = new int[s_names.Length];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = i;
			}
			Array.Sort(array, (int a, int b) => s_acc[b].CompareTo(s_acc[a]));
			double num = s_frameAcc / (double)s_frames;
			double num2 = 0.0;
			double[] array2 = s_acc;
			foreach (double num4 in array2)
			{
				num2 += num4;
			}
			num2 /= (double)s_frames;
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append($"цикл кадра за {s_frames} кадров: кадр {num:0.0} мс, из них подсистемы {num2:0.0} мс, прочее {num - num2:0.0} мс. Самое тяжёлое: ");
			int num5 = 0;
			int[] array3 = array;
			foreach (int num6 in array3)
			{
				double num7 = s_acc[num6] / (double)s_frames;
				if (num7 < 0.05 || num5 >= top)
				{
					break;
				}
				string value;
				string text = (s_hints.TryGetValue(s_names[num6], out value) ? (" (" + value + ")") : "");
				stringBuilder.Append(string.Format("{0}{1}{2} {3:0.00} мс", (num5 > 0) ? "; " : "", s_names[num6], text, num7));
				num5++;
			}
			Array.Clear(s_acc, 0, s_acc.Length);
			s_frameAcc = 0.0;
			s_frames = 0;
			return stringBuilder.ToString();
		}
	}
	internal static class MethodProfiler
	{
		private sealed class Acc
		{
			public long Ticks;

			public long Calls;

			public string Name;
		}

		private static readonly Dictionary<MethodBase, Acc> s_acc = new Dictionary<MethodBase, Acc>();

		private static Harmony s_harmony;

		private static bool s_on;

		private static int s_startFrame;

		private static readonly string[] s_types = new string[75]
		{
			"MonoUpdaters", "Character", "Humanoid", "Player", "ZSyncTransform", "ZSyncAnimation", "CharacterAnimEvent", "BaseAI", "MonsterAI", "AnimalAI",
			"Tameable", "Procreation", "Floating", "Aoe", "EffectArea", "WaterVolume", "Smoke", "SmokeSpawner", "SmokeRenderer", "ZSFX",
			"VisEquipment", "FootStep", "InstanceRenderer", "LightFlicker", "LightLod", "CraftingStation", "ZNetScene", "ZNet", "ZDOMan", "ZoneSystem",
			"EnvMan", "Hud", "Minimap", "WearNTearUpdater", "WearNTear", "Pathfinding", "RenderGroupSystem", "MaterialMan", "TerrainLod", "ReflectionUpdate",
			"CinderSpawner", "ParticleMist", "MistEmitter", "DistantFogEmitter", "Vine", "GameCamera", "ClutterSystem", "Heightmap", "Fireplace", "PrivateArea",
			"Container", "ItemDrop", "Pickable", "Plant", "EnemyHud", "DamageText", "Chat", "MessageHud", "AudioMan", "MusicMan",
			"Game", "RandEventSystem", "Thunder", "ObjectFlicker", "Vagon", "Ship", "Fish", "FishingFloat", "Ragdoll", "Projectile",
			"AmplifyOcclusionEffect", "CameraEffects", "UpscaledFrameBuffer", "PresentManager", "SystemResourceManager"
		};

		private static readonly string[] s_methods = new string[16]
		{
			"Update", "FixedUpdate", "LateUpdate", "CustomUpdate", "CustomFixedUpdate", "CustomLateUpdate", "UpdateAI", "StaticUpdate", "UpdateFloaters", "UpdateMaterials",
			"UpdateWear", "UpdateLights", "OnPreRender", "OnPostRender", "OnRenderImage", "OnPreCull"
		};

		public static bool On => s_on;

		public static string Start()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			if (s_on)
			{
				return "замер уже идёт: pb prof — отчёт, pb prof off — выключить";
			}
			s_harmony = new Harmony("qua8ion.valheim.piecebatcher.prof");
			s_acc.Clear();
			HarmonyMethod val = new HarmonyMethod(typeof(MethodProfiler), "Prefix", (Type[])null);
			HarmonyMethod val2 = new HarmonyMethod(typeof(MethodProfiler), "Postfix", (Type[])null);
			int num = 0;
			int num2 = 0;
			Assembly assembly = typeof(WearNTear).Assembly;
			string[] array = s_types;
			foreach (string text in array)
			{
				Type type = assembly.GetType(text);
				if (type == null)
				{
					continue;
				}
				string[] array2 = s_methods;
				foreach (string text2 in array2)
				{
					MethodInfo method;
					try
					{
						method = type.GetMethod(text2, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					}
					catch (AmbiguousMatchException)
					{
						continue;
					}
					if (!(method == null) && !method.IsGenericMethod && !method.IsAbstract)
					{
						try
						{
							s_harmony.Patch((MethodBase)method, val, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
							s_acc[method] = new Acc
							{
								Name = text + "." + text2
							};
							num++;
						}
						catch (Exception)
						{
							num2++;
						}
					}
				}
			}
			s_on = true;
			s_startFrame = Time.frameCount;
			return string.Format("замер методов включён: {0} методов{1}. pb prof — отчёт за время с прошлого отчёта, pb prof off — выключить", num, (num2 > 0) ? $", не удалось {num2}" : "");
		}

		public static string Stop()
		{
			if (!s_on)
			{
				return "замер методов не включён";
			}
			string text = Report();
			s_harmony.UnpatchSelf();
			s_harmony = null;
			s_on = false;
			s_acc.Clear();
			return text + Environment.NewLine + "замер методов выключен";
		}

		public static void ResetWindow()
		{
			foreach (Acc value in s_acc.Values)
			{
				value.Ticks = 0L;
				value.Calls = 0L;
			}
			s_startFrame = Time.frameCount;
		}

		public static string Report(int top = 25)
		{
			if (!s_on)
			{
				return "замер методов не включён: pb prof on";
			}
			int num = Time.frameCount - s_startFrame;
			if (num < 10)
			{
				return "замер методов: мало кадров, подожди";
			}
			List<Acc> list = new List<Acc>(s_acc.Values);
			list.Sort((Acc a, Acc b) => b.Ticks.CompareTo(a.Ticks));
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append($"методы игры за {num} кадров (мс на кадр, вызовов на кадр; вложенные считаются дважды): ");
			int num2 = 0;
			foreach (Acc item in list)
			{
				double num3 = (double)item.Ticks * 1000.0 / (double)Stopwatch.Frequency / (double)num;
				if (num3 < 0.02 || num2 >= top)
				{
					break;
				}
				stringBuilder.Append(string.Format("{0}{1} {2:0.00} мс ({3:0.#})", (num2 > 0) ? "; " : "", item.Name, num3, (double)item.Calls / (double)num));
				num2++;
			}
			foreach (Acc item2 in list)
			{
				item2.Ticks = 0L;
				item2.Calls = 0L;
			}
			s_startFrame = Time.frameCount;
			return stringBuilder.ToString();
		}

		private static void Prefix(out long __state)
		{
			__state = Stopwatch.GetTimestamp();
		}

		private static void Postfix(MethodBase __originalMethod, long __state)
		{
			if (s_acc.TryGetValue(__originalMethod, out var value))
			{
				value.Ticks += Stopwatch.GetTimestamp() - __state;
				value.Calls++;
			}
		}
	}
	internal sealed class PbCommand : ConsoleCommand
	{
		public override string Name => "pb";

		public override string Help => "PieceBatcher: pb [stats|scene|loop|prof on|prof off|prof|sync|cell|on|off|rebuild]";

		public override void Run(string[] args)
		{
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			string text = ((args.Length != 0) ? args[0].ToLowerInvariant() : "stats");
			string text2 = ((args.Length > 1) ? args[1].ToLowerInvariant() : "");
			switch (text)
			{
			case "on":
				Plugin.Enabled.Value = true;
				Print("PieceBatcher включён, ячейки пересобираются");
				break;
			case "off":
				Plugin.Enabled.Value = false;
				Print("PieceBatcher выключен, детали рисуются как в игре");
				break;
			case "rebuild":
				Batcher.MarkAllDirty(now: true);
				Print("все ячейки будут пересобраны");
				break;
			case "cell":
				Print(((Object)(object)Player.m_localPlayer != (Object)null) ? Batcher.CellStats(((Component)Player.m_localPlayer).transform.position) : "нет игрока");
				break;
			case "scene":
				Print(Batcher.Stats());
				Print(Diag.FrameLine());
				Print(Diag.SceneLine());
				break;
			case "loop":
				Print(Diag.FrameLine());
				Print(LoopProbes.Report());
				break;
			case "prof":
				if (text2 == "on")
				{
					Print(MethodProfiler.Start());
					break;
				}
				if (text2 == "off")
				{
					Print(MethodProfiler.Stop());
					break;
				}
				Print(Diag.FrameLine());
				Print(MethodProfiler.Report());
				break;
			case "sync":
				Print(SyncSkip.Report());
				break;
			default:
				Print(Batcher.Stats());
				Print(SyncSkip.Report());
				Print(Diag.FrameLine());
				break;
			}
		}

		public override List<string> CommandOptionList()
		{
			return new List<string> { "stats", "scene", "loop", "prof", "sync", "cell", "on", "off", "rebuild" };
		}

		private static void Print(string s)
		{
			Plugin.Log.LogInfo((object)s);
			if ((Object)(object)Console.instance != (Object)null)
			{
				Console.instance.Print(s);
			}
		}
	}
	internal sealed class MeshData
	{
		public const int AttrNormals = 1;

		public const int AttrTangents = 2;

		public const int AttrColors = 4;

		public const int AttrUv0 = 8;

		public Vector3[] Pos;

		public Vector3[] Nrm;

		public Vector4[] Tan;

		public Color32[] Col;

		public Vector4[][] Uv = new Vector4[4][];

		public int[] UvDim = new int[4];

		public int[][] Sub;

		public int Attr;

		public int Signature;

		public bool Ok;
	}
	internal static class MeshCache
	{
		private static readonly Dictionary<int, MeshData> s_cache = new Dictionary<int, MeshData>();

		private static readonly List<Vector3> s_v3 = new List<Vector3>();

		private static readonly List<Vector4> s_v4 = new List<Vector4>();

		private static readonly List<Color32> s_c32 = new List<Color32>();

		private static readonly List<int> s_int = new List<int>();

		public static int Count => s_cache.Count;

		public static MeshData Get(Mesh mesh)
		{
			int instanceID = ((Object)mesh).GetInstanceID();
			if (s_cache.TryGetValue(instanceID, out var value))
			{
				return value;
			}
			value = Read(mesh);
			s_cache[instanceID] = value;
			return value;
		}

		public static void Clear()
		{
			s_cache.Clear();
		}

		private static MeshData Read(Mesh mesh)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			MeshData meshData = new MeshData();
			if (!mesh.isReadable || mesh.vertexCount == 0 || mesh.subMeshCount == 0)
			{
				return meshData;
			}
			for (int i = 0; i < mesh.subMeshCount; i++)
			{
				if ((int)mesh.GetTopology(i) != 0)
				{
					return meshData;
				}
			}
			s_v3.Clear();
			mesh.GetVertices(s_v3);
			meshData.Pos = s_v3.ToArray();
			if (mesh.HasVertexAttribute((VertexAttribute)1))
			{
				s_v3.Clear();
				mesh.GetNormals(s_v3);
				if (s_v3.Count == meshData.Pos.Length)
				{
					meshData.Nrm = s_v3.ToArray();
					meshData.Attr |= 1;
				}
			}
			if (mesh.HasVertexAttribute((VertexAttribute)2))
			{
				s_v4.Clear();
				mesh.GetTangents(s_v4);
				if (s_v4.Count == meshData.Pos.Length)
				{
					meshData.Tan = s_v4.ToArray();
					meshData.Attr |= 2;
				}
			}
			if (mesh.HasVertexAttribute((VertexAttribute)3))
			{
				s_c32.Clear();
				mesh.GetColors(s_c32);
				if (s_c32.Count == meshData.Pos.Length)
				{
					meshData.Col = s_c32.ToArray();
					meshData.Attr |= 4;
				}
			}
			for (int j = 0; j < 4; j++)
			{
				VertexAttribute val = (VertexAttribute)(4 + j);
				if (mesh.HasVertexAttribute(val))
				{
					s_v4.Clear();
					mesh.GetUVs(j, s_v4);
					if (s_v4.Count == meshData.Pos.Length)
					{
						meshData.Uv[j] = s_v4.ToArray();
						meshData.UvDim[j] = Mathf.Clamp(mesh.GetVertexAttributeDimension(val), 2, 4);
						meshData.Attr |= 8 << j;
					}
				}
			}
			meshData.Sub = new int[mesh.subMeshCount][];
			for (int k = 0; k < mesh.subMeshCount; k++)
			{
				s_int.Clear();
				mesh.GetTriangles(s_int, k, true);
				meshData.Sub[k] = s_int.ToArray();
			}
			meshData.Signature = meshData.Attr | (meshData.UvDim[0] << 8) | (meshData.UvDim[1] << 10) | (meshData.UvDim[2] << 12) | (meshData.UvDim[3] << 14);
			meshData.Ok = true;
			return meshData;
		}
	}
	[HarmonyPatch]
	internal static class Patches
	{
		[HarmonyPatch(typeof(WearNTear), "Awake")]
		[HarmonyPostfix]
		private static void WearNTear_Awake(WearNTear __instance, ZNetView ___m_nview)
		{
			if ((Object)(object)___m_nview != (Object)null && ___m_nview.GetZDO() != null)
			{
				Batcher.Register(__instance);
			}
		}

		[HarmonyPatch(typeof(WearNTear), "OnDestroy")]
		[HarmonyPrefix]
		private static void WearNTear_OnDestroy(WearNTear __instance)
		{
			Batcher.Unregister(__instance);
		}

		[HarmonyPatch(typeof(WearNTear), "SetHealthVisual")]
		[HarmonyPrefix]
		private static void SetHealthVisual_Prefix(WearNTear __instance, out int __state)
		{
			__state = Batcher.VariantState(__instance);
		}

		[HarmonyPatch(typeof(WearNTear), "SetHealthVisual")]
		[HarmonyPostfix]
		private static void SetHealthVisual_Postfix(WearNTear __instance, int __state)
		{
			if (__state != Batcher.VariantState(__instance))
			{
				Batcher.OnChanged(__instance);
			}
		}

		[HarmonyPatch(typeof(WearNTear), "Highlight")]
		[HarmonyPrefix]
		private static void Highlight_Prefix(WearNTear __instance)
		{
			Batcher.SetHighlighted(__instance, on: true);
		}

		[HarmonyPatch(typeof(WearNTear), "ResetHighlight")]
		[HarmonyPostfix]
		private static void ResetHighlight_Postfix(WearNTear __instance)
		{
			Batcher.SetHighlighted(__instance, on: false);
		}

		[HarmonyPatch(typeof(WearNTear), "SetAshlandsMaterialValue")]
		[HarmonyPostfix]
		private static void SetAshlandsMaterialValue_Postfix(WearNTear __instance, float v)
		{
			Batcher.SetMaterialTouched(__instance, v != 0f);
		}

		[HarmonyPatch(typeof(Container), "UpdateUseVisual")]
		[HarmonyPrefix]
		private static void Container_UpdateUseVisual_Prefix(Container __instance, out int __state)
		{
			__state = Batcher.ContainerState(__instance);
		}

		[HarmonyPatch(typeof(Container), "UpdateUseVisual")]
		[HarmonyPostfix]
		private static void Container_UpdateUseVisual_Postfix(Container __instance, int __state)
		{
			if (__state != Batcher.ContainerState(__instance))
			{
				WearNTear component = ((Component)__instance).GetComponent<WearNTear>();
				if ((Object)(object)component != (Object)null)
				{
					Batcher.OnChanged(component);
				}
			}
		}

		[HarmonyPatch(typeof(WearNTear), "RPC_CreateFragments")]
		[HarmonyPrefix]
		private static void RPC_CreateFragments_Prefix(WearNTear __instance)
		{
			Batcher.OnBeforeFragments(__instance);
		}

		[HarmonyPatch(typeof(WearNTear), "RPC_CreateFragments")]
		[HarmonyPostfix]
		private static void RPC_CreateFragments_Postfix()
		{
			Batcher.OnAfterFragments();
		}

		[HarmonyPatch(typeof(Destructible), "CreateFragments", new Type[]
		{
			typeof(GameObject),
			typeof(bool)
		})]
		[HarmonyPrefix]
		private static void CreateFragments_Prefix(ref bool visibleOnly)
		{
			if (Batcher.ConsumeForceFragments())
			{
				visibleOnly = false;
			}
		}

		[HarmonyPatch(typeof(ZNetScene), "OnDestroy")]
		[HarmonyPrefix]
		private static void ZNetScene_OnDestroy()
		{
			Batcher.Clear();
			SyncSkip.Clear();
		}

		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		[HarmonyPostfix]
		private static void ZNetScene_Awake()
		{
			if (Plugin.ProfilerEnabled.Value && !MethodProfiler.On)
			{
				Plugin.Log.LogInfo((object)MethodProfiler.Start());
			}
		}

		[HarmonyPatch(typeof(Player), "OnSpawned")]
		[HarmonyPostfix]
		private static void Player_OnSpawned(Player __instance)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				MethodProfiler.ResetWindow();
				LoopProbes.ResetWindow();
			}
		}
	}
	[BepInPlugin("qua8ion.valheim.piecebatcher", "PieceBatcher", "1.2.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "qua8ion.valheim.piecebatcher";

		public const string PluginName = "PieceBatcher";

		public const string PluginVersion = "1.2.1";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<float> CellSize;

		internal static ConfigEntry<float> RebuildDelay;

		internal static ConfigEntry<float> FrameBudgetMs;

		internal static ConfigEntry<int> MinParts;

		internal static ConfigEntry<int> MaxVertices;

		internal static ConfigEntry<string> Shaders;

		internal static ConfigEntry<string> ExcludePrefabs;

		internal static ConfigEntry<bool> Verbose;

		internal static ConfigEntry<bool> LoopProbesEnabled;

		internal static ConfigEntry<bool> ProfilerEnabled;

		internal static ConfigEntry<bool> SyncSkipEnabled;

		internal static ConfigEntry<bool> SyncBatchPhysics;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Expected O, but got Unknown
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Expected O, but got Unknown
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Expected O, but got Unknown
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Expected O, but got Unknown
			//IL_035e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0368: Expected O, but got Unknown
			//IL_0393: Unknown result type (might be due to invalid IL or missing references)
			//IL_0398: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a6: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Склеивать детали построек в общие меши (та же картинка, меньше вызовов отрисовки). Переключается на лету.");
			CellSize = ((BaseUnityPlugin)this).Config.Bind<float>("General", "CellSize", 16f, new ConfigDescription("Размер ячейки склейки в метрах. Меньше ячейка — точнее отсечение за кадром, но больше мешей.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(4f, 64f), Array.Empty<object>()));
			RebuildDelay = ((BaseUnityPlugin)this).Config.Bind<float>("General", "RebuildDelay", 2f, new ConfigDescription("Через сколько секунд после последнего изменения в ячейке (постройка, снос, износ) она пересобирается.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 30f), Array.Empty<object>()));
			FrameBudgetMs = ((BaseUnityPlugin)this).Config.Bind<float>("General", "FrameBudgetMs", 3f, new ConfigDescription("Сколько миллисекунд за кадр можно тратить на пересборку ячеек.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 50f), Array.Empty<object>()));
			MinParts = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MinParts", 2, new ConfigDescription("Минимум деталей одного материала в ячейке, чтобы их склеивать.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 50), Array.Empty<object>()));
			MaxVertices = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaxVertices", 60000, new ConfigDescription("Потолок вершин одного склеенного меша; ячейка с большим числом делится на несколько.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5000, 1000000), Array.Empty<object>()));
			Shaders = ((BaseUnityPlugin)this).Config.Bind<string>("Filters", "Shaders", "Custom/Piece", "Шейдеры материалов, которые можно склеивать, через запятую. Остальные детали рисуются как в игре.");
			ExcludePrefabs = ((BaseUnityPlugin)this).Config.Bind<string>("Filters", "ExcludePrefabs", "", "Имена префабов, которые не склеивать, через запятую (например, piece_chest_wood).");
			SyncSkipEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "SyncSkip", true, "Пропускать шаг сетевой синхронизации у чужих объектов, которые стоят на месте: новых данных с сервера нет, объект уже в цели, тело спит. Шаг ничего бы не изменил; как только что-то меняется, синхронизация идёт как обычно.");
			SyncBatchPhysics = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "SyncBatchPhysics", true, "Внутри шага сетевой синхронизации передавать физике сдвинутые объекты одним вызовом в конце шага, а не после каждого объекта. Физика видит те же позиции в тот же момент.");
			Verbose = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Verbose", false, "Писать в лог каждую пересборку ячейки.");
			LoopProbesEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LoopProbes", true, "Держать пробы в цикле кадра Unity для команды pb loop (физика, скрипты, анимация, частицы, рендер, ожидание GPU). Стоят доли микросекунды на кадр.");
			LoopProbesEnabled.SettingChanged += delegate
			{
				if (LoopProbesEnabled.Value)
				{
					LoopProbes.Install();
				}
				else
				{
					LoopProbes.Uninstall();
				}
			};
			ProfilerEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "MethodProfiler", true, "Включать секундомер на методах игры при входе в мир, чтобы pb prof сразу показывал отчёт. pb prof off снимает его до следующего входа; стоит доли микросекунды на вызов.");
			Enabled.SettingChanged += delegate
			{
				Batcher.OnEnabledChanged();
			};
			CellSize.SettingChanged += delegate
			{
				Batcher.Reset();
			};
			Shaders.SettingChanged += delegate
			{
				Batcher.Reset();
			};
			ExcludePrefabs.SettingChanged += delegate
			{
				Batcher.Reset();
			};
			MinParts.SettingChanged += delegate
			{
				Batcher.MarkAllDirty();
			};
			MaxVertices.SettingChanged += delegate
			{
				Batcher.MarkAllDirty();
			};
			_harmony = new Harmony("qua8ion.valheim.piecebatcher");
			_harmony.CreateClassProcessor(typeof(Patches)).Patch();
			SyncSkip.Install(_harmony);
			GameObject val = new GameObject("PieceBatcher")
			{
				hideFlags = (HideFlags)61
			};
			Object.DontDestroyOnLoad((Object)val);
			val.AddComponent<BatcherRunner>();
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new PbCommand());
			if (LoopProbesEnabled.Value)
			{
				try
				{
					LoopProbes.Install();
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("пробы цикла кадра не встали: " + ex.GetType().Name + ": " + ex.Message));
				}
			}
			Log.LogInfo((object)"PieceBatcher 1.2.1 загружен");
		}

		private void OnDestroy()
		{
			LoopProbes.Uninstall();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal sealed class BatcherRunner : MonoBehaviour
	{
		private void Update()
		{
			Diag.Tick();
			Batcher.Update();
		}

		private void LateUpdate()
		{
			Batcher.LateUpdate();
		}

		private void OnApplicationQuit()
		{
			Batcher.Quitting = true;
		}
	}
	internal static class SyncSkip
	{
		private sealed class State
		{
			public bool Idle;

			public uint Revision;

			public Vector3 Pos;

			public Quaternion Rot;
		}

		[HarmonyPatch]
		private static class Patches
		{
			[HarmonyPatch(typeof(ZSyncTransform), "CustomFixedUpdate")]
			[HarmonyPrefix]
			private static bool CustomFixedUpdate_Prefix(ZSyncTransform __instance, ZNetView ___m_nview, Rigidbody ___m_body)
			{
				//IL_008f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0095: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
				if (!Plugin.SyncSkipEnabled.Value)
				{
					return true;
				}
				Calls++;
				if (!s_states.TryGetValue(__instance, out var value) || !value.Idle)
				{
					return true;
				}
				if ((Object)(object)___m_nview == (Object)null || !___m_nview.IsValid())
				{
					return true;
				}
				ZDO zDO = ___m_nview.GetZDO();
				if (zDO.DataRevision != value.Revision || zDO.IsOwner())
				{
					value.Idle = false;
					return true;
				}
				if ((Object)(object)___m_body != (Object)null && !___m_body.IsSleeping())
				{
					value.Idle = false;
					return true;
				}
				Transform transform = ((Component)__instance).transform;
				if (transform.position != value.Pos || transform.rotation != value.Rot)
				{
					value.Idle = false;
					return true;
				}
				Skipped++;
				return false;
			}

			[HarmonyPatch(typeof(ZSyncTransform), "CustomFixedUpdate")]
			[HarmonyPostfix]
			private static void CustomFixedUpdate_Postfix(ZSyncTransform __instance, ZNetView ___m_nview, Rigidbody ___m_body, bool ___m_isKinematicBody)
			{
				//IL_009b: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
				//IL_007d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0082: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
				//IL_0126: Unknown result type (might be due to invalid IL or missing references)
				//IL_012b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0132: Unknown result type (might be due to invalid IL or missing references)
				//IL_0137: Unknown result type (might be due to invalid IL or missing references)
				if (!Plugin.SyncSkipEnabled.Value)
				{
					return;
				}
				if (!s_states.TryGetValue(__instance, out var value))
				{
					value = new State();
					s_states[__instance] = value;
				}
				if (value.Idle || (Object)(object)___m_nview == (Object)null || !___m_nview.IsValid())
				{
					return;
				}
				ZDO zDO = ___m_nview.GetZDO();
				if (zDO.IsOwner() || ___m_isKinematicBody || __instance.m_syncScale || __instance.m_syncBodyVelocity)
				{
					return;
				}
				if (__instance.m_characterParentSync && zDO.HasOwner())
				{
					ZDOID connectionZDOID = zDO.GetConnectionZDOID((ConnectionType)2);
					if (!((ZDOID)(ref connectionZDOID)).IsNone())
					{
						return;
					}
				}
				if (zDO.HasOwner())
				{
					Vector3 vec = zDO.GetVec3(ZDOVars.s_velHash, Vector3.zero);
					if (((Vector3)(ref vec)).sqrMagnitude > 0f)
					{
						return;
					}
				}
				Transform transform = ((Component)__instance).transform;
				if ((!__instance.m_syncPosition || !(Vector3.Distance(transform.position, zDO.GetPosition()) > 0.001f)) && (!__instance.m_syncRotation || !(Quaternion.Angle(transform.rotation, zDO.GetRotation()) > 0.001f)) && (!((Object)(object)___m_body != (Object)null) || ___m_body.IsSleeping()))
				{
					value.Idle = true;
					value.Revision = zDO.DataRevision;
					value.Pos = transform.position;
					value.Rot = transform.rotation;
				}
			}

			[HarmonyPatch(typeof(ZSyncTransform), "OnDisable")]
			[HarmonyPostfix]
			private static void OnDisable_Postfix(ZSyncTransform __instance)
			{
				s_states.Remove(__instance);
			}

			[HarmonyPatch(typeof(MonoUpdatersExtra), "CustomFixedUpdate")]
			[HarmonyPrefix]
			private static void Loop_Prefix(List<IMonoUpdater> source)
			{
				if (source == ZSyncTransform.Instances)
				{
					s_inSyncLoop = true;
				}
			}

			[HarmonyPatch(typeof(MonoUpdatersExtra), "CustomFixedUpdate")]
			[HarmonyPostfix]
			private static void Loop_Postfix(List<IMonoUpdater> source)
			{
				if (s_inSyncLoop)
				{
					s_inSyncLoop = false;
					FlushPhysicsSync();
				}
			}

			[HarmonyPatch(typeof(ZSyncTransform), "SyncPosition")]
			[HarmonyTranspiler]
			private static IEnumerable<CodeInstruction> SyncPosition_Transpiler(IEnumerable<CodeInstruction> instructions)
			{
				MethodInfo target = AccessTools.Method(typeof(Physics), "SyncTransforms", (Type[])null, (Type[])null);
				MethodInfo replacement = AccessTools.Method(typeof(SyncSkip), "DeferPhysicsSync", (Type[])null, (Type[])null);
				foreach (CodeInstruction instruction in instructions)
				{
					if (target != null && CodeInstructionExtensions.Calls(instruction, target))
					{
						instruction.opcode = OpCodes.Call;
						instruction.operand = replacement;
						TranspiledCalls++;
					}
					yield return instruction;
				}
			}
		}

		private static readonly Dictionary<ZSyncTransform, State> s_states = new Dictionary<ZSyncTransform, State>();

		internal static bool Installed;

		internal static long Calls;

		internal static long Skipped;

		private static long s_callsAtReport;

		private static long s_skippedAtReport;

		private static int s_frameAtReport;

		private static bool s_inSyncLoop;

		private static bool s_pendingSync;

		internal static int TranspiledCalls;

		internal static long Deferred;

		internal static long Flushes;

		private static long s_deferredAtReport;

		private static long s_flushesAtReport;

		internal static void DeferPhysicsSync()
		{
			if (s_inSyncLoop && Plugin.SyncBatchPhysics.Value)
			{
				s_pendingSync = true;
				Deferred++;
			}
			else
			{
				Physics.SyncTransforms();
			}
		}

		private static void FlushPhysicsSync()
		{
			if (s_pendingSync)
			{
				s_pendingSync = false;
				Physics.SyncTransforms();
				Flushes++;
			}
		}

		internal static void Install(Harmony harmony)
		{
			try
			{
				harmony.CreateClassProcessor(typeof(Patches)).Patch();
				Installed = true;
				if (TranspiledCalls == 0)
				{
					Plugin.Log.LogWarning((object)"в ZSyncTransform.SyncPosition не нашлось Physics.SyncTransforms(): отложенная синхронизация физики выключена");
				}
			}
			catch (Exception ex)
			{
				Installed = false;
				Plugin.Log.LogWarning((object)("пропуск синхронизации не встал (игра изменилась?): " + ex.GetType().Name + ": " + ex.Message));
			}
		}

		internal static string Report()
		{
			if (!Installed)
			{
				return "синхронизация: пропуск не установлен";
			}
			if (!Plugin.SyncSkipEnabled.Value)
			{
				return "синхронизация: пропуск выключен в конфиге";
			}
			long num = Calls - s_callsAtReport;
			long num2 = Skipped - s_skippedAtReport;
			long num3 = Deferred - s_deferredAtReport;
			long num4 = Flushes - s_flushesAtReport;
			int num5 = Mathf.Max(1, Time.frameCount - s_frameAtReport);
			s_callsAtReport = Calls;
			s_skippedAtReport = Skipped;
			s_deferredAtReport = Deferred;
			s_flushesAtReport = Flushes;
			s_frameAtReport = Time.frameCount;
			double num6 = ((num > 0) ? (100.0 * (double)num2 / (double)num) : 0.0);
			string text = ((TranspiledCalls > 0) ? $"; синхронизаций физики вместо {num3} сделано {num4} ({(double)num3 / (double)num5:0.#} → {(double)num4 / (double)num5:0.#} на кадр)" : "; отложенная синхронизация физики не установлена");
			return $"синхронизация стоящих объектов: пропущено {num2} из {num} шагов ({num6:0}%), {(double)num2 / (double)num5:0.#} из {(double)num / (double)num5:0.#} на кадр, стоят на месте {CountIdle()} из {s_states.Count}{text}";
		}

		private static int CountIdle()
		{
			int num = 0;
			foreach (State value in s_states.Values)
			{
				if (value.Idle)
				{
					num++;
				}
			}
			return num;
		}

		internal static void Clear()
		{
			s_states.Clear();
			s_inSyncLoop = false;
			s_pendingSync = false;
		}
	}
}